<?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=Oxygene</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=Oxygene"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/Oxygene"/>
	<updated>2026-04-11T08:40:40Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Halbleiter&amp;diff=4868</id>
		<title>Halbleiter</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Halbleiter&amp;diff=4868"/>
		<updated>2004-11-25T14:28:44Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: Umlaute sind von der Rechtschreibreform noch nicht betroffen ;)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Eigenschaften ==&lt;br /&gt;
&lt;br /&gt;
Halbleiter besitzen eine geringere Leitfähigkeit als normale Metalle, was an der viel niedrigeren Ladungsträgerkonzentration liegt. Ein weiteres Merkmal ist, dass die Leitfähigkeit im Gegensatz zu Metallen mit der Temperatur steigt (Heissleiter), da durch die Temperaturerhöhung mehr Ladungsträger aktiviert werden.&lt;br /&gt;
&lt;br /&gt;
Wichtige Halbleitermaterialien:&lt;br /&gt;
* Si, GaAs, InP, Ge, GaP, InSb, SiC, GaN&lt;br /&gt;
&lt;br /&gt;
== Dotieren ==&lt;br /&gt;
&lt;br /&gt;
Um die Ladungsträgerkonzentration und damit die Leitfähigkeit von Halbleitern zu erhöhen, bringt man gezielt Fremdatome in das Material ein; diesen Vorgang nennt man &amp;quot;Dotieren&amp;quot;. Bei den Fremdatomen unterscheidet man zwischen &amp;quot;Donatoren&amp;quot;, die ein Elektron zur Verfügung stellen, und &amp;quot;Akzeptoren&amp;quot;, die ein Elektron aufnehmen können (bzw. ein &amp;quot;Loch&amp;quot; zur Verfügung stellen. Einen überwiegend mit Donatoren dotierten Halbleiter bezeichnet man als n-Halbleiter, einen überwiegend mit Akzeptoren dotierten als p-Halbleiter. Undotierte Halbleiter werden auch &amp;quot;intrinsische Halbleiter&amp;quot; genannt.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise zum Dotieren verwendete Elemente:&lt;br /&gt;
&lt;br /&gt;
*Donatoren: P, As, Sb, Si, S&lt;br /&gt;
*Akzeptoren: B, Al, Ga, C, Si, Mn&lt;br /&gt;
&lt;br /&gt;
(Si kann in GaAs je nach Einbauplatz als Donator oder Akzeptor wirken)&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die gesamte Halbleiterelektronik beruht auf Halbleitern - daher wohl auch der Name. Nahezu alle aktiven Bauelemente sind Halbleiter: &lt;br /&gt;
&lt;br /&gt;
*[[Diode]]&lt;br /&gt;
*[[Transistor]]&lt;br /&gt;
*hochintegrierte Schaltungen [[IC]]&lt;br /&gt;
*und natürlich die hier besonders interessierenden [[Mikrocontroller]]&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4849</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=4849"/>
		<updated>2004-11-18T16:50:17Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* EEPROM */  typos&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschinencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom Sendedatenregister in das Send-Schieberegister übernommen wurde und der UART nun wieder bereit ist, ein neues Zeichen zum Senden aufzunehmen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
UBRR = \frac{Taktfrequenz}{Baudrate * 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    while (!(USR &amp;amp; (1&amp;lt;&amp;lt;UDRE))); /* warten bis Senden moeglich                   */&lt;br /&gt;
    UDR = &#039;x&#039;;                  /* Schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void&lt;br /&gt;
uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while(!(USR &amp;amp; (1 &amp;lt;&amp;lt; UDRE)))&lt;br /&gt;
    ;   /* warte, bis UDR bereit */&lt;br /&gt;
&lt;br /&gt;
    UDR = c;    /* sende Zeichen */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void&lt;br /&gt;
uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != NULL */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem 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;
:Meine Empfehlung ist deswegen klar 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;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&lt;br /&gt;
&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei, 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATmega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sog. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&lt;br /&gt;
&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll, ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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-Kondition, entsprechend der Konfiguration, 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-Kondition, entsprechend der Konfiguration, 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&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&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 der 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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen, müssen wir die Includedatei &#039;&#039;interrupt.h&#039;&#039; und evtl. &#039;&#039;signal.h&#039;&#039; einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, dass vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgegeben sind, die innerhalb weniger Taktzyklen aufeinanderfolgen müssen (&amp;quot;timed sequence&amp;quot;). Auch dies muss bei Nutzung der Funktionen aus der avr-libc/eeprom.h-Datei nicht selbst implementiert werden. 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;
&amp;lt;!-- kann bald geloescht werden:&lt;br /&gt;
Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;
&amp;lt;!-- altes Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
&amp;lt;/pre&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4826</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=4826"/>
		<updated>2004-11-15T15:36:44Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Optimierungsgrad */  typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschinencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom Sendedatenregister in das Send-Schieberegister übernommen wurde und der UART nun wieder bereit ist, ein neues Zeichen zum Senden aufzunehmen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
UBRR = \frac{Taktfrequenz}{Baudrate * 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem 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;
:Meine Empfehlung ist deswegen klar 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;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&lt;br /&gt;
&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei, 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATmega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sog. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&lt;br /&gt;
&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll, ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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-Kondition, entsprechend der Konfiguration, 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-Kondition, entsprechend der Konfiguration, 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&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&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 der 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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen, müssen wir die Includedatei &#039;&#039;interrupt.h&#039;&#039; und evtl. &#039;&#039;signal.h&#039;&#039; einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:AVR-GCC-Tutorial&amp;diff=4806</id>
		<title>Diskussion:AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:AVR-GCC-Tutorial&amp;diff=4806"/>
		<updated>2004-11-15T01:21:01Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Warum hier im Codebsp. die Frequenz gesetzt wenn man im Einfache Wandlung (Single Conversion)-modus ist?&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteile setzen auf 8 (1)&lt;br /&gt;
&lt;br /&gt;
Weil man immer den Vorteiler entsprechend der Quarzfrequenz setzen muß um den ADC nicht mit einem zu hohen Takt zu versorgen. --[[Benutzer:Matthias|Matthias]] 19:58, 21. Sep 2004 (CEST)&lt;br /&gt;
&lt;br /&gt;
Ich benutze folgenden code der funktioniert. Es wird kein vorteiler gesetzt!!&lt;br /&gt;
&lt;br /&gt;
    sbi(ADCSR,ADEN);   //ADC An&lt;br /&gt;
    sbi(ADMUX,MUX0);	//kanal wählen&lt;br /&gt;
    for (;;) {                           /* loop forever */&lt;br /&gt;
           	sbi(ADCSR,ADSC);				//wandlung starten&lt;br /&gt;
    	    	while(bit_is_set(ADCSR,ADSC)); 	//warten bis wandlung fertig&lt;br /&gt;
    		num = ADCW;&lt;br /&gt;
&lt;br /&gt;
--- schnipp ---&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler hat nichts mit &amp;quot;funktionieren&amp;quot; oder &amp;quot;nicht funktionieren&amp;quot; zu tun. Man sollte sich eher Gedanken um die Genauigkeit und einzuhaltende Maximalfrequenzen bei der Wandlung machen. Der Abschnitt &amp;quot;ADC/Prescaling and Conversion Time&amp;quot; im Datenblatt gibt eigentlich erschoepfend Auskunft darueber. Das &amp;quot;sbi&amp;quot; in dem Beispielcode oben in einem Diskussionsbeitrag zum gcc-tutorial noch auftaucht ist mir schleierhaft, da kann ich mir die Aktualisierung auch sparen. Falls das im Artikel gezeigte Beispiel nachweislich falsch sein sollte: bitte aendern.&lt;br /&gt;
MfG M. Thomas&lt;br /&gt;
&lt;br /&gt;
Butterfly schoen und gut, aber diese beiden Hinweise in letzter Zeit mit der Preisangabe &amp;quot;30Eur&amp;quot;... Ist das irgendwo ein Lager &amp;quot;zu voll&amp;quot; und wird hier versucht &amp;quot;etwas Werbung&amp;quot; zu machen? Der Preis ist &amp;quot;normal&amp;quot; aber wenn man sich umschaut und/oder sich mit ein paar Leuten für eine Sammelbestellung zusammentut gibt es die Teile deutlich guenstiger.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
Einige der Links im Kapitel 11.1.1 (Messen eines Widerstandes) funktionieren nicht. Dies wären:&lt;br /&gt;
* http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c&lt;br /&gt;
* http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h&lt;br /&gt;
* http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile&lt;br /&gt;
&lt;br /&gt;
--[[Benutzer:Oxygene|Oxygene]] 12:05, 12. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
Habe die Tabellen übersichtlicher gemacht, überflüssige Tabellen durch gewöhnliche Einrückungen ersetzt und recht viele Kommafehler bei Konditionalsätzen berichtigt (so um die 762 *g*). Inhaltlich ist alles gleich geblieben.&lt;br /&gt;
--[[Benutzer:Oxygene|Oxygene]] 15:01, 12. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
Danke fuer die Korrekturen, einige der Fehler sind sicher &amp;quot;auf meinem Mist gewachsen&amp;quot;. Die Links zu &amp;quot;Messen eines Widerstands&amp;quot; sollten ganz raus, entweder ein aktualisiertes Beispiel direkt im Text oder in einem anderen Wiki-Artikel oder ersatzlos streichen. Schlussendlich ist es ein avr-gcc Tutorial die Beispiele sollten avr-gcc-Spezifisches erläutern und den manchmal etwas &amp;quot;trockenen&amp;quot; Text auflockern. Wie man einzelne Komponenten des AVR ansteuert hat nur in seltenen Fällen mit der Impementierungssprache zu tun. Hier vor allem dann, wenn die avr-libc entsprechende &amp;quot;APIs&amp;quot; zur Verfügung stellt. Alles Andere gehört eigentlich in ein &amp;quot;sprachunabhängiges&amp;quot; AVR-Tutorial. Das avr-gcc Tutorial &amp;quot;schleppt&amp;quot; mein Meinung in dieser Hinsicht noch einige Altlasten mit. [[Benutzer:mthomas]]&lt;br /&gt;
&lt;br /&gt;
Also ich habe das Tut durchgearbeitet und alle mir auffallenden Fehler korrigiert. Man hat da sofort bemerkt, von wo an die Texte von dir geschrieben wurden, da die Kommafehler schlagartig aufhörten ;)&lt;br /&gt;
Da sind auch ein paar Formulierungen, die ich persönlich nicht so passend finde (&amp;quot;voll krass&amp;quot;), die wollte ich aber nicht ändern, da das hier eher dein Baby ist, es sei denn, du hast nichts dagegen, dass man da etwas rumbastelt. Will dir hier nicht dazwischenpfuschen und deine Pläne durcheinander bringen.&lt;br /&gt;
--[[Benutzer:Oxygene|Oxygene]] 02:21, 15. Nov 2004 (CET)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Hauptseite&amp;diff=4798</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Hauptseite&amp;diff=4798"/>
		<updated>2004-11-14T11:18:41Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: werbung gelöscht&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{| width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;vertical-align:top&amp;quot; |&lt;br /&gt;
&amp;lt;div style=&amp;quot;margin: 0; margin-right:10px; border: 1px solid #dfdfdf; padding: 0 1em 1em 1em; background-color:#F8F8FF; align:right;&amp;quot;&amp;gt;&lt;br /&gt;
== Willkommen beim Mikrocontroller-Wiki == &lt;br /&gt;
Ein Wiki ist eine Informationssammlung, an der jeder mitwirken kann - sogar ohne Anmeldung. Es soll kein Lexikon sein, sondern eher eine Sammlung von informativen Artikeln rund um das Thema Mikrocontroller &amp;amp; Elektronik. Wer eigene Projekte, Anleitungen oder Tipps &amp;amp; Tricks veröffentlichen möchte ist hier genau richtig.&lt;br /&gt;
&lt;br /&gt;
Im Moment existieren {{NUMBEROFARTICLES}} Seiten.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;margin: 0; margin-top:10px; margin-right:10px; border: 1px solid #dfdfdf; padding: 0em 1em 1em 1em; background-color:#FFfFeF; align:right;&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wo geht&#039;s los? ==&lt;br /&gt;
&lt;br /&gt;
Ein guter Einstieg ist die [[Mikrocontroller]]-Seite. Wer nicht weiß, nach welchen Kriterien er einen Mikrocontroller auswählen soll, der findet in [[Entscheidung Mikrocontroller]] einige Hinweise. Verschiedene Programmiersprachen werden [[HLL|hier]] verglichen.&lt;br /&gt;
&lt;br /&gt;
Es gibt Erklärungen wie man [[SMD Löten|SMD lötet]] und was die Bezeichnungen auf den [[SMD]]-Bauteilen bedeuten.&lt;br /&gt;
&lt;br /&gt;
An einen Mikrocontroller kann man natürlich vieles anschließen: Man kann ihn per [[USB]] mit dem PC verbinden, man kann ihn an den [[TV-out|Fernseher]] anschließen oder einfach nur einen  der zahlreichen [[Temperatursensor]]en benutzen.&lt;br /&gt;
&lt;br /&gt;
Überwältigt von den vielen Abkürzungen und Fachbegriffen? Hier wird natürlich unter anderem auch erklärt, was [[USART]], [[Brownout]], [[I2C|I²C]], [[SPI]], [[JTAG]] und [[CRC]] bedeuten.&lt;br /&gt;
&lt;br /&gt;
Für diverse Dinge rund um den AVR, beginnend bei einem Tutorial, gibt es eine [[AVR-Linkseite]].&lt;br /&gt;
&lt;br /&gt;
Eine Liste interessanter Links zu Web-Seiten für diverse Controller-Architekturen findet sich in der [[Linksammlung]].&lt;br /&gt;
&lt;br /&gt;
Zur Bauteilbeschaffung sind auf der Seite [[Elektronik-Versender]] einige Links zu Anbietern zusammengestellt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Noch nicht genug? Eine alphabetische Liste mit allen Themen liefert die [http://www.mikrocontroller.net/wikisoftware/index.php?title=Spezial:Allpages&amp;amp;from=0 Artikelübersicht].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;40%&amp;quot; style=&amp;quot;vertical-align:top&amp;quot; |&lt;br /&gt;
&amp;lt;div style=&amp;quot;margin:0;  border:1px solid #dfdfdf; padding: 0em 1em 1em 1em; background-color:#efefef; align:left;&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Kann ich wirklich &amp;quot;einfach so&amp;quot; irgendetwas an den Seiten ändern? ===&lt;br /&gt;
&lt;br /&gt;
Ja. Um eine Seite zu ändern reicht ein Klick auf den &amp;quot;Seite bearbeiten&amp;quot; Link.&lt;br /&gt;
Aber: Bitte lies Dir vorher die [[Uc-wiki:Wie man eine Seite bearbeitet|Bearbeitungshinweise]] durch und schau Dir am besten mal ein paar der anderen Seiten an, um zu sehen wie das Ganze funktioniert.&lt;br /&gt;
&lt;br /&gt;
=== Super, dann kann ich ja gleich mein altes Elektroniklexikon hier reintippen! ===&lt;br /&gt;
&lt;br /&gt;
NEIN! Bitte verwende nur Texte an denen du selber die Rechte hast.&lt;br /&gt;
&lt;br /&gt;
=== Gibt es einen Testbereich, wo man das Ganze mal ausprobieren kann? ===&lt;br /&gt;
&lt;br /&gt;
Ja, dafür gibt es die [[Testseite]].&lt;br /&gt;
&lt;br /&gt;
=== Wie kann ich neue Seiten erstellen? ===&lt;br /&gt;
&lt;br /&gt;
Gib einfach den gewünschten Titel in die Suche ein und klicke auf &amp;quot;Los&amp;quot;, falls die Seite noch nicht existiert findest du dann dort einen Link zum Anlegen der Seite. Bitte schau dir erst den Aufbau von ein paar exisiterenden Seiten an (auf &amp;quot;Seite bearbeiten&amp;quot; klicken, dann wird der Quelltext angezeit), um herauszufinden wie das mit den Überschriften und Formatierungen funktioniert.&lt;br /&gt;
Mehr Informationen zu den Formatierungsmöglichkeiten gibt es [[Uc-wiki:Wie man eine Seite bearbeitet|hier]].&lt;br /&gt;
&lt;br /&gt;
=== Wozu ist der &amp;quot;Diskussion&amp;quot;-Link? ===&lt;br /&gt;
&lt;br /&gt;
Auf den Diskussionsseiten kann man Kommentare, Kritik oder Fragen zum jeweiligen Artikel unterbringen.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
__NOTOC__&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Spannungsreferenz&amp;diff=9378</id>
		<title>Spannungsreferenz</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Spannungsreferenz&amp;diff=9378"/>
		<updated>2004-11-13T19:26:02Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: spielkind&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Eine Spannungsreferenz ist eine Schaltung oder ein Bauelement, das eine [[Spannung]] definierter Höhe bereitstellt.&lt;br /&gt;
&lt;br /&gt;
Sehr viele elektronische Schaltungen arbeiten nur &amp;quot;relativ&amp;quot;, das heißt zum Beispiel&lt;br /&gt;
* ein Netzteil, das eine stabilisierte Betriebsspannung für einen [[Mikrocontroller]] liefern soll, hält diese tatsächlich nur in Relation zu einem Vergleichswert konstant&lt;br /&gt;
* ein [[ADC|Analog-Digital-Converter]], der einen analogen Spannungswert messen soll, ermittelt den digitalen Wert tatsächlich nur im Relation zu einer Bezugsgröße&lt;br /&gt;
&lt;br /&gt;
Als Vergleichs- und Bezugsgrößen eignet sich in einfachen Fällen &lt;br /&gt;
* eine (oder mehrere) in Durchlassrichtung geschaltete [[Diode]](n)&lt;br /&gt;
* eine [[Z-Diode]].&lt;br /&gt;
Durch Herstellungstoleranzen und Temperatureinflüsse ist die Genaugkeit hier allerdings recht beschränkt.&lt;br /&gt;
&lt;br /&gt;
Für höhere Ansprüche gibt es spezielle Bauteile in Form [[IC|integrierter Schaltungen]] (z.B. LM336, LM385). Herstellungstoleranzen werden dabei durch einen genauen Abgleich bei (bzw. nach) der Produktion noch im Hause des Herstellers ausgeglichen. Dem Temperaturdrift wirken geeignete Schaltungsmaßnahmen entgegen (oder solche Bauteile heizen sich selbst auf eine bestimmte Temperatur auf, die sie dann konstant halten).&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:AVR-GCC-Tutorial&amp;diff=4801</id>
		<title>Diskussion:AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:AVR-GCC-Tutorial&amp;diff=4801"/>
		<updated>2004-11-12T14:01:34Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Warum hier im Codebsp. die Frequenz gesetzt wenn man im Einfache Wandlung (Single Conversion)-modus ist?&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteile setzen auf 8 (1)&lt;br /&gt;
&lt;br /&gt;
Weil man immer den Vorteiler entsprechend der Quarzfrequenz setzen muß um den ADC nicht mit einem zu hohen Takt zu versorgen. --[[Benutzer:Matthias|Matthias]] 19:58, 21. Sep 2004 (CEST)&lt;br /&gt;
&lt;br /&gt;
Ich benutze folgenden code der funktioniert. Es wird kein vorteiler gesetzt!!&lt;br /&gt;
&lt;br /&gt;
    sbi(ADCSR,ADEN);   //ADC An&lt;br /&gt;
    sbi(ADMUX,MUX0);	//kanal wählen&lt;br /&gt;
    for (;;) {                           /* loop forever */&lt;br /&gt;
           	sbi(ADCSR,ADSC);				//wandlung starten&lt;br /&gt;
    	    	while(bit_is_set(ADCSR,ADSC)); 	//warten bis wandlung fertig&lt;br /&gt;
    		num = ADCW;&lt;br /&gt;
&lt;br /&gt;
--- schnipp ---&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler hat nichts mit &amp;quot;funktionieren&amp;quot; oder &amp;quot;nicht funktionieren&amp;quot; zu tun. Man sollte sich eher Gedanken um die Genauigkeit und einzuhaltende Maximalfrequenzen bei der Wandlung machen. Der Abschnitt &amp;quot;ADC/Prescaling and Conversion Time&amp;quot; im Datenblatt gibt eigentlich erschoepfend Auskunft darueber. Das &amp;quot;sbi&amp;quot; in dem Beispielcode oben in einem Diskussionsbeitrag zum gcc-tutorial noch auftaucht ist mir schleierhaft, da kann ich mir die Aktualisierung auch sparen. Falls das im Artikel gezeigte Beispiel nachweislich falsch sein sollte: bitte aendern.&lt;br /&gt;
MfG M. Thomas&lt;br /&gt;
&lt;br /&gt;
Butterfly schoen und gut, aber diese beiden Hinweise in letzter Zeit mit der Preisangabe &amp;quot;30Eur&amp;quot;... Ist das irgendwo ein Lager &amp;quot;zu voll&amp;quot; und wird hier versucht &amp;quot;etwas Werbung&amp;quot; zu machen? Der Preis ist &amp;quot;normal&amp;quot; aber wenn man sich umschaut und/oder sich mit ein paar Leuten für eine Sammelbestellung zusammentut gibt es die Teile deutlich guenstiger.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
Einige der Links im Kapitel 11.1.1 (Messen eines Widerstandes) funktionieren nicht. Dies wären:&lt;br /&gt;
* http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c&lt;br /&gt;
* http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h&lt;br /&gt;
* http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile&lt;br /&gt;
&lt;br /&gt;
--[[Benutzer:Oxygene|Oxygene]] 12:05, 12. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
Habe die Tabellen übersichtlicher gemacht, überflüssige Tabellen durch gewöhnliche Einrückungen ersetzt und recht viele Kommafehler bei Konditionalsätzen berichtigt (so um die 762 *g*). Inhaltlich ist alles gleich geblieben.&lt;br /&gt;
--[[Benutzer:Oxygene|Oxygene]] 15:01, 12. Nov 2004 (CET)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4788</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=4788"/>
		<updated>2004-11-12T13:49:19Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Assembler und Inline-Assembler */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom Sendedatenregister in das Send-Schieberegister übernommen wurde und der UART nun wieder bereit ist, ein neues Zeichen zum Senden aufzunehmen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
UBBR = \frac{Taktfrequenz}{Baudrate * 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem 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;
:Meine Empfehlung ist deswegen klar 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;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&lt;br /&gt;
&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei, 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATmega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sog. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&lt;br /&gt;
&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll, ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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-Kondition, entsprechend der Konfiguration, 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-Kondition, entsprechend der Konfiguration, 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&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&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 der 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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen, müssen wir die Includedatei &#039;&#039;interrupt.h&#039;&#039; und evtl. &#039;&#039;signal.h&#039;&#039; einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen einer NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4780</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=4780"/>
		<updated>2004-11-12T13:45:17Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* EEPROM */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom Sendedatenregister in das Send-Schieberegister übernommen wurde und der UART nun wieder bereit ist, ein neues Zeichen zum Senden aufzunehmen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
UBBR = \frac{Taktfrequenz}{Baudrate * 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem 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;
:Meine Empfehlung ist deswegen klar 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;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&lt;br /&gt;
&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei, 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATmega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sog. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&lt;br /&gt;
&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll, ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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-Kondition, entsprechend der Konfiguration, 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-Kondition, entsprechend der Konfiguration, 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&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&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 der 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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen, müssen wir die Includedatei &#039;&#039;interrupt.h&#039;&#039; und evtl. &#039;&#039;signal.h&#039;&#039; einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4779</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=4779"/>
		<updated>2004-11-12T13:38:57Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Programmspeicher (Flash) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom Sendedatenregister in das Send-Schieberegister übernommen wurde und der UART nun wieder bereit ist, ein neues Zeichen zum Senden aufzunehmen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
UBBR = \frac{Taktfrequenz}{Baudrate * 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem 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;
:Meine Empfehlung ist deswegen klar 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;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&lt;br /&gt;
&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei, 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATmega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sog. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&lt;br /&gt;
&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll, ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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-Kondition, entsprechend der Konfiguration, 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-Kondition, entsprechend der Konfiguration, 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&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&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 der 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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen, müssen wir die Includedatei &#039;&#039;interrupt.h&#039;&#039; und evtl. &#039;&#039;signal.h&#039;&#039; einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel, jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4778</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=4778"/>
		<updated>2004-11-12T13:29:23Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Programmieren mit Interrupts */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom Sendedatenregister in das Send-Schieberegister übernommen wurde und der UART nun wieder bereit ist, ein neues Zeichen zum Senden aufzunehmen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
UBBR = \frac{Taktfrequenz}{Baudrate * 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem 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;
:Meine Empfehlung ist deswegen klar 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;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&lt;br /&gt;
&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei, 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATmega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sog. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&lt;br /&gt;
&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll, ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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-Kondition, entsprechend der Konfiguration, 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-Kondition, entsprechend der Konfiguration, 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&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&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 der 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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen, rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen, werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig, muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen, müssen wir die Includedatei &#039;&#039;interrupt.h&#039;&#039; und evtl. &#039;&#039;signal.h&#039;&#039; einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht, als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus, oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&lt;br /&gt;
&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4777</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=4777"/>
		<updated>2004-11-12T13:11:03Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Der Watchdog */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom Sendedatenregister in das Send-Schieberegister übernommen wurde und der UART nun wieder bereit ist, ein neues Zeichen zum Senden aufzunehmen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
UBBR = \frac{Taktfrequenz}{Baudrate * 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem 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;
:Meine Empfehlung ist deswegen klar 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;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&lt;br /&gt;
&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei, 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATmega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sog. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&lt;br /&gt;
&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&lt;br /&gt;
&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde, löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden, um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDTOE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
:Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDE&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt wird, so wird der Watchdog aktiviert.&lt;br /&gt;
:Das Bit kann nur gelöscht werden, solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WDP2&#039;&#039;&#039;, &#039;&#039;&#039;WDP1&#039;&#039;&#039;, &#039;&#039;&#039;WDP0&#039;&#039;&#039; (&#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden, muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;wdt_enable(uint8_t timeout)&#039;&#039;&#039;&lt;br /&gt;
:Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
* &#039;&#039;&#039;wdt_disable()&#039;&#039;&#039;&lt;br /&gt;
:Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
* &#039;&#039;&#039;wdt_reset()&#039;&#039;&#039;&lt;br /&gt;
:Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll, ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei interrupt.h und evtl. signal.h einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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 den Zustand 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). 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4776</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=4776"/>
		<updated>2004-11-12T13:02:38Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Sleep-Modes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom Sendedatenregister in das Send-Schieberegister übernommen wurde und der UART nun wieder bereit ist, ein neues Zeichen zum Senden aufzunehmen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
UBBR = \frac{Taktfrequenz}{Baudrate * 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem 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;
:Meine Empfehlung ist deswegen klar 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;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&lt;br /&gt;
&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei, 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATmega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sog. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;set_sleep_mode(uint8_t mode)&#039;&#039;&#039;&lt;br /&gt;
:Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B. SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;sleep_mode()&#039;&#039;&#039;&lt;br /&gt;
:Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei interrupt.h und evtl. signal.h einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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 den Zustand 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). 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4775</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=4775"/>
		<updated>2004-11-12T12:59:30Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Die Timer/Counter des AVR */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom Sendedatenregister in das Send-Schieberegister übernommen wurde und der UART nun wieder bereit ist, ein neues Zeichen zum Senden aufzunehmen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
UBBR = \frac{Taktfrequenz}{Baudrate * 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem 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;
:Meine Empfehlung ist deswegen klar 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;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&lt;br /&gt;
&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei, 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATmega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;COM1A1&#039;&#039;&#039;, &#039;&#039;&#039;COM1A0&#039;&#039;&#039; (&#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits)&lt;br /&gt;
:Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
:Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert (Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 1 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;nicht invertierende PWM&#039;&#039;.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht, so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht, so wird der Pin auf 0 gesetzt.&lt;br /&gt;
&lt;br /&gt;
Man nennt dies &#039;&#039;invertierende PWM&#039;&#039;.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PWM11&#039;&#039;&#039;, &#039;&#039;&#039;PWM10&#039;&#039;&#039; (&#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits)&lt;br /&gt;
:Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICNC1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler (4 CKs) Timer/Counter 1&lt;br /&gt;
:oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
:Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICES1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1) oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CTC1&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
:Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird, ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
:Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&lt;br /&gt;
:... | C-2 | C-1 | C | 0 | 1 |...&lt;br /&gt;
:Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&lt;br /&gt;
:... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1, C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&lt;br /&gt;
:In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS12&#039;&#039;&#039;, &#039;&#039;&#039;CS11&#039;&#039;&#039;, &#039;&#039;&#039;CS10&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat, beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein, so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll, müssen ebenfalls alle Interrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäß Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird, so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt, müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird, so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8-, 9- oder 10-Bit PWM verwendet wird, und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; gespeicherten Wert erreicht, wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw. gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert, so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet, kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind, wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCIE1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein Vergleichswert definiert werden.&lt;br /&gt;
&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird beim Erreichen des Vergleichswertes ein Compare Match Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TICIE&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein Capture Event Interrupt ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP) auftritt. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein, wenn auch ein entsprechender Interrupt ausgelöst werden soll.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOIE0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird bei einem Überlauf des Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt ausgelöst. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV1&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OCF1A&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039; übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ICF1&#039;&#039;&#039; (&#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist, welches anzeigt, dass der Wert des Datenregisters des  Timer/Counter 1 in das Input Capture Register ICR1 übertragen wurde.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TOV0&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein Überlauf des Datenregisters stattfindet.&lt;br /&gt;
&lt;br /&gt;
:Das Flag wird automatisch gelöscht, wenn der zugehörige Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht werden, indem eine logische 1 (!) in das entsprechende Bit geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei interrupt.h und evtl. signal.h einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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 den Zustand 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). 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4774</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=4774"/>
		<updated>2004-11-12T12:26:17Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Die Register des ADC */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom Sendedatenregister in das Send-Schieberegister übernommen wurde und der UART nun wieder bereit ist, ein neues Zeichen zum Senden aufzunehmen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
UBBR = \frac{Taktfrequenz}{Baudrate * 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\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;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| style=&amp;quot;&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem 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;
:Meine Empfehlung ist deswegen klar 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;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&lt;br /&gt;
&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei, 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATMega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits&lt;br /&gt;
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert&lt;br /&gt;
(Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
| In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 1 gesetzt.&lt;br /&gt;
Man nennt dies nicht invertierende PWM.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 0 gesetzt.&lt;br /&gt;
Man nennt dies invertierende PWM.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PWM11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler&lt;br /&gt;
(4 CKs) Timer/Counter 1&amp;lt;br /&amp;gt;&lt;br /&gt;
oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1)&lt;br /&gt;
oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input&lt;br /&gt;
Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2 | C-1 | C | 0 | 1 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1,&lt;br /&gt;
C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS12&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein&lt;br /&gt;
Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet&lt;br /&gt;
ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäss Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler&lt;br /&gt;
betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
gespeicherten Wert erreicht wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw.&lt;br /&gt;
gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik&lt;br /&gt;
zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein&lt;br /&gt;
Vergleichswert definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes&lt;br /&gt;
ein Compare Match Interrupt ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt&lt;br /&gt;
ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP)&lt;br /&gt;
auftritt. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein,&lt;br /&gt;
wenn auch ein entsprechender Interrupt ausgelöst werden soll.&amp;lt;font face=&amp;quot;Helvetica&amp;quot; size=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung&lt;br /&gt;
von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters&lt;br /&gt;
von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
übereinstimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist,&lt;br /&gt;
welches anzeigt, dass der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 in das Input Capture Register ICR1 übertragen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei interrupt.h und evtl. signal.h einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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 den Zustand 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). 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4773</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=4773"/>
		<updated>2004-11-12T12:26:06Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Die Timer/Counter des AVR */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom Sendedatenregister in das Send-Schieberegister übernommen wurde und der UART nun wieder bereit ist, ein neues Zeichen zum Senden aufzunehmen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
UBBR = \frac{Taktfrequenz}{Baudrate * 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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&lt;br /&gt;
: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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports&lt;br /&gt;
eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen&lt;br /&gt;
Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
:Meine Empfehlung ist deswegen klar diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang&lt;br /&gt;
verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller herauslesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&lt;br /&gt;
&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&lt;br /&gt;
&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei, 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&lt;br /&gt;
&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CS02, CS01, CS00&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits)&lt;br /&gt;
:Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&amp;lt;dl&amp;gt;&amp;lt;dd&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/dd&amp;gt;&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
:Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang geschaltet ist.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATMega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits&lt;br /&gt;
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert&lt;br /&gt;
(Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
| In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 1 gesetzt.&lt;br /&gt;
Man nennt dies nicht invertierende PWM.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 0 gesetzt.&lt;br /&gt;
Man nennt dies invertierende PWM.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PWM11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler&lt;br /&gt;
(4 CKs) Timer/Counter 1&amp;lt;br /&amp;gt;&lt;br /&gt;
oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1)&lt;br /&gt;
oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input&lt;br /&gt;
Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2 | C-1 | C | 0 | 1 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1,&lt;br /&gt;
C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS12&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein&lt;br /&gt;
Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet&lt;br /&gt;
ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäss Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler&lt;br /&gt;
betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
gespeicherten Wert erreicht wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw.&lt;br /&gt;
gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik&lt;br /&gt;
zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein&lt;br /&gt;
Vergleichswert definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes&lt;br /&gt;
ein Compare Match Interrupt ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt&lt;br /&gt;
ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP)&lt;br /&gt;
auftritt. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein,&lt;br /&gt;
wenn auch ein entsprechender Interrupt ausgelöst werden soll.&amp;lt;font face=&amp;quot;Helvetica&amp;quot; size=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung&lt;br /&gt;
von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters&lt;br /&gt;
von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
übereinstimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist,&lt;br /&gt;
welches anzeigt, dass der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 in das Input Capture Register ICR1 übertragen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei interrupt.h und evtl. signal.h einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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 den Zustand 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). 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4772</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=4772"/>
		<updated>2004-11-12T12:06:31Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Allgemeines zum UART */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von einem 11-Bit Zeichenrahmen:&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation eintreffen. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom Sendedatenregister in das Send-Schieberegister übernommen wurde und der UART nun wieder bereit ist, ein neues Zeichen zum Senden aufzunehmen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\begin{matrix}&lt;br /&gt;
UBBR = \frac{Taktfrequenz}{Baudrate * 16} - 1&lt;br /&gt;
\end{matrix}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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&lt;br /&gt;
: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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports&lt;br /&gt;
eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen&lt;br /&gt;
Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
:Meine Empfehlung ist deswegen klar diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang&lt;br /&gt;
verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller raus lesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS02&amp;lt;br /&amp;gt;&lt;br /&gt;
CS01&amp;lt;br /&amp;gt;&lt;br /&gt;
CS00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird&lt;br /&gt;
ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang&lt;br /&gt;
geschaltet ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATMega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits&lt;br /&gt;
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert&lt;br /&gt;
(Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
| In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 1 gesetzt.&lt;br /&gt;
Man nennt dies nicht invertierende PWM.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 0 gesetzt.&lt;br /&gt;
Man nennt dies invertierende PWM.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PWM11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler&lt;br /&gt;
(4 CKs) Timer/Counter 1&amp;lt;br /&amp;gt;&lt;br /&gt;
oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1)&lt;br /&gt;
oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input&lt;br /&gt;
Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2 | C-1 | C | 0 | 1 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1,&lt;br /&gt;
C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS12&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein&lt;br /&gt;
Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet&lt;br /&gt;
ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäss Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler&lt;br /&gt;
betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
gespeicherten Wert erreicht wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw.&lt;br /&gt;
gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik&lt;br /&gt;
zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein&lt;br /&gt;
Vergleichswert definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes&lt;br /&gt;
ein Compare Match Interrupt ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt&lt;br /&gt;
ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP)&lt;br /&gt;
auftritt. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein,&lt;br /&gt;
wenn auch ein entsprechender Interrupt ausgelöst werden soll.&amp;lt;font face=&amp;quot;Helvetica&amp;quot; size=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung&lt;br /&gt;
von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters&lt;br /&gt;
von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
übereinstimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist,&lt;br /&gt;
welches anzeigt, dass der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 in das Input Capture Register ICR1 übertragen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei interrupt.h und evtl. signal.h einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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 den Zustand 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). 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4771</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=4771"/>
		<updated>2004-11-12T12:02:20Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Die Register des ADC */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt&lt;br /&gt;
:ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global&lt;br /&gt;
:Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt&lt;br /&gt;
:ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global&lt;br /&gt;
:Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer&lt;br /&gt;
:Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues&lt;br /&gt;
:zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt&lt;br /&gt;
:Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART&lt;br /&gt;
:überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende&lt;br /&gt;
:Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART&lt;br /&gt;
:überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende&lt;br /&gt;
:Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen&lt;br /&gt;
:und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches&lt;br /&gt;
:Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von&lt;br /&gt;
:einem 11-Bit Zeichenrahmen:&amp;lt;br /&amp;gt;&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses&lt;br /&gt;
:Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses&lt;br /&gt;
:Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor&lt;br /&gt;
:das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom&lt;br /&gt;
:Empfangs-Schieberegister in das Empfangs-Datenregister transferiert&lt;br /&gt;
:wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister&lt;br /&gt;
:ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres&lt;br /&gt;
:Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation&lt;br /&gt;
:eintreffen. Mit dem Auslesen des Datenregisters wird das Bit&lt;br /&gt;
:automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister&lt;br /&gt;
:befindliche Zeichen vollständig ausgegeben wurde und kein weiteres&lt;br /&gt;
:Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die&lt;br /&gt;
:Kommunikation vollumfänglich abgeschlossen ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das&lt;br /&gt;
:Programm nach dem Senden von Daten auf Empfang schalten muss. Im&lt;br /&gt;
:Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende&lt;br /&gt;
:Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit&lt;br /&gt;
:selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom&lt;br /&gt;
:Sendedatenregister in das Send-Schieberegister übernommen wurde und&lt;br /&gt;
:der UART nun wieder bereit ist, ein neues Zeichen zum Senden&lt;br /&gt;
:aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das&lt;br /&gt;
:Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen&lt;br /&gt;
:Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines&lt;br /&gt;
:empfangenen Zeichens 0 ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen&lt;br /&gt;
:Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im&lt;br /&gt;
:Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das&lt;br /&gt;
:nachfolgende Zeichen komplett empfangen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in&lt;br /&gt;
:das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
UBRR = Taktfrequenz / (Baudrate * 16) - 1&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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&lt;br /&gt;
: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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports&lt;br /&gt;
eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen&lt;br /&gt;
Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
:Meine Empfehlung ist deswegen klar diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang&lt;br /&gt;
verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller raus lesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS02&amp;lt;br /&amp;gt;&lt;br /&gt;
CS01&amp;lt;br /&amp;gt;&lt;br /&gt;
CS00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird&lt;br /&gt;
ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang&lt;br /&gt;
geschaltet ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATMega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits&lt;br /&gt;
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert&lt;br /&gt;
(Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
| In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 1 gesetzt.&lt;br /&gt;
Man nennt dies nicht invertierende PWM.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 0 gesetzt.&lt;br /&gt;
Man nennt dies invertierende PWM.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PWM11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler&lt;br /&gt;
(4 CKs) Timer/Counter 1&amp;lt;br /&amp;gt;&lt;br /&gt;
oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1)&lt;br /&gt;
oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input&lt;br /&gt;
Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2 | C-1 | C | 0 | 1 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1,&lt;br /&gt;
C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS12&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein&lt;br /&gt;
Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet&lt;br /&gt;
ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäss Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler&lt;br /&gt;
betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
gespeicherten Wert erreicht wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw.&lt;br /&gt;
gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik&lt;br /&gt;
zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein&lt;br /&gt;
Vergleichswert definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes&lt;br /&gt;
ein Compare Match Interrupt ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt&lt;br /&gt;
ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP)&lt;br /&gt;
auftritt. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein,&lt;br /&gt;
wenn auch ein entsprechender Interrupt ausgelöst werden soll.&amp;lt;font face=&amp;quot;Helvetica&amp;quot; size=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung&lt;br /&gt;
von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters&lt;br /&gt;
von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
übereinstimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist,&lt;br /&gt;
welches anzeigt, dass der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 in das Input Capture Register ICR1 übertragen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei interrupt.h und evtl. signal.h einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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 den Zustand 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). 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4770</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=4770"/>
		<updated>2004-11-12T12:00:31Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Analoge Ein- und Ausgabe */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
ToDo Bsp Quellcode&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt&lt;br /&gt;
:ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global&lt;br /&gt;
:Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt&lt;br /&gt;
:ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global&lt;br /&gt;
:Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer&lt;br /&gt;
:Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues&lt;br /&gt;
:zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt&lt;br /&gt;
:Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART&lt;br /&gt;
:überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende&lt;br /&gt;
:Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART&lt;br /&gt;
:überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende&lt;br /&gt;
:Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen&lt;br /&gt;
:und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches&lt;br /&gt;
:Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von&lt;br /&gt;
:einem 11-Bit Zeichenrahmen:&amp;lt;br /&amp;gt;&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses&lt;br /&gt;
:Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses&lt;br /&gt;
:Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor&lt;br /&gt;
:das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom&lt;br /&gt;
:Empfangs-Schieberegister in das Empfangs-Datenregister transferiert&lt;br /&gt;
:wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister&lt;br /&gt;
:ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres&lt;br /&gt;
:Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation&lt;br /&gt;
:eintreffen. Mit dem Auslesen des Datenregisters wird das Bit&lt;br /&gt;
:automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister&lt;br /&gt;
:befindliche Zeichen vollständig ausgegeben wurde und kein weiteres&lt;br /&gt;
:Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die&lt;br /&gt;
:Kommunikation vollumfänglich abgeschlossen ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das&lt;br /&gt;
:Programm nach dem Senden von Daten auf Empfang schalten muss. Im&lt;br /&gt;
:Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende&lt;br /&gt;
:Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit&lt;br /&gt;
:selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom&lt;br /&gt;
:Sendedatenregister in das Send-Schieberegister übernommen wurde und&lt;br /&gt;
:der UART nun wieder bereit ist, ein neues Zeichen zum Senden&lt;br /&gt;
:aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das&lt;br /&gt;
:Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen&lt;br /&gt;
:Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines&lt;br /&gt;
:empfangenen Zeichens 0 ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen&lt;br /&gt;
:Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im&lt;br /&gt;
:Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das&lt;br /&gt;
:nachfolgende Zeichen komplett empfangen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in&lt;br /&gt;
:das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
UBRR = Taktfrequenz / (Baudrate * 16) - 1&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039;ADC&#039;&#039;&#039; oder &#039;&#039;&#039;DAC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um, welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann, wird durch die Auflösung des &#039;&#039;&#039;ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039;ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/256 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375, 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird 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).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inkl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde, muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 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&lt;br /&gt;
: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;Fr&#039;&#039;&#039;ee Running Select)&lt;br /&gt;
:Mit diesem Bit wird die Betriebsart eingestellt.&lt;br /&gt;
:Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst nun ständig den ausgewählten Kanal und schreibt den gemessenen Wert in das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039;.&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, wenn eine Umwandlung erfolgt und das &#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&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 1 in das Register geschrieben wird (So steht es in der AVR-Dokumentation).&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 sein.&lt;br /&gt;
:Der Vorteiler muss also so eingestellt werden, dass die CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert zwischen 50-200kHz 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;
\\&lt;br /&gt;
TF_{min}=\frac{CLK}{200kHz}=\frac{4000000}{200000}=\mathbf{20}&lt;br /&gt;
\\&lt;br /&gt;
\\&lt;br /&gt;
TF_{max}=\frac{CLK}{50kHz}=\frac{4000000}{50000}=\mathbf{80}&lt;br /&gt;
\end{matrix}&lt;br /&gt;
\\&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 128&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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MUX2...MUX0&#039;&#039;&#039;&lt;br /&gt;
:Mit diesem 3 Bits wird der zu messende Kanal bestimmt. Es wird einfach die entsprechende Pinnummer des Ports&lt;br /&gt;
eingeschrieben.&lt;br /&gt;
:Wenn das Register beschrieben wird, während dem eine Umwandlung läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen&lt;br /&gt;
Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu berücksichtigen.&lt;br /&gt;
:Meine Empfehlung ist deswegen klar diese, dass der frei laufende Betrieb nur bei einem einzelnen zu verwendenden Analogeingang&lt;br /&gt;
verwendet werden sollte, wenn man sich Probleme bei der Umschalterei ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);              //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel;    // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);              // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));      // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));     // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));   // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
    result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);             // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;                     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren, müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW, wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
Hinweis:&lt;br /&gt;
:In den folgenden Überlegungen wird als Controller der 90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039;PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039;PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;/pre&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;pre class=&amp;quot;code&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;/pre&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;
&amp;lt;!--Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet. [oxygene: Dieser Absatz trifft wohl nur auf __outw zu]&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller raus lesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS02&amp;lt;br /&amp;gt;&lt;br /&gt;
CS01&amp;lt;br /&amp;gt;&lt;br /&gt;
CS00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird&lt;br /&gt;
ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang&lt;br /&gt;
geschaltet ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATMega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits&lt;br /&gt;
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert&lt;br /&gt;
(Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
| In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 1 gesetzt.&lt;br /&gt;
Man nennt dies nicht invertierende PWM.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 0 gesetzt.&lt;br /&gt;
Man nennt dies invertierende PWM.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PWM11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler&lt;br /&gt;
(4 CKs) Timer/Counter 1&amp;lt;br /&amp;gt;&lt;br /&gt;
oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1)&lt;br /&gt;
oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input&lt;br /&gt;
Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2 | C-1 | C | 0 | 1 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1,&lt;br /&gt;
C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS12&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein&lt;br /&gt;
Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet&lt;br /&gt;
ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäss Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler&lt;br /&gt;
betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
gespeicherten Wert erreicht wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw.&lt;br /&gt;
gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik&lt;br /&gt;
zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein&lt;br /&gt;
Vergleichswert definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes&lt;br /&gt;
ein Compare Match Interrupt ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt&lt;br /&gt;
ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP)&lt;br /&gt;
auftritt. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein,&lt;br /&gt;
wenn auch ein entsprechender Interrupt ausgelöst werden soll.&amp;lt;font face=&amp;quot;Helvetica&amp;quot; size=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung&lt;br /&gt;
von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters&lt;br /&gt;
von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
übereinstimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist,&lt;br /&gt;
welches anzeigt, dass der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 in das Input Capture Register ICR1 übertragen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei interrupt.h und evtl. signal.h einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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 den Zustand 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). 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:AVR-GCC-Tutorial&amp;diff=4781</id>
		<title>Diskussion:AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:AVR-GCC-Tutorial&amp;diff=4781"/>
		<updated>2004-11-12T11:06:12Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Warum hier im Codebsp. die Frequenz gesetzt wenn man im Einfache Wandlung (Single Conversion)-modus ist?&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteile setzen auf 8 (1)&lt;br /&gt;
&lt;br /&gt;
Weil man immer den Vorteiler entsprechend der Quarzfrequenz setzen muß um den ADC nicht mit einem zu hohen Takt zu versorgen. --[[Benutzer:Matthias|Matthias]] 19:58, 21. Sep 2004 (CEST)&lt;br /&gt;
&lt;br /&gt;
Ich benutze folgenden code der funktioniert. Es wird kein vorteiler gesetzt!!&lt;br /&gt;
&lt;br /&gt;
    sbi(ADCSR,ADEN);   //ADC An&lt;br /&gt;
    sbi(ADMUX,MUX0);	//kanal wählen&lt;br /&gt;
    for (;;) {                           /* loop forever */&lt;br /&gt;
           	sbi(ADCSR,ADSC);				//wandlung starten&lt;br /&gt;
    	    	while(bit_is_set(ADCSR,ADSC)); 	//warten bis wandlung fertig&lt;br /&gt;
    		num = ADCW;&lt;br /&gt;
&lt;br /&gt;
--- schnipp ---&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler hat nichts mit &amp;quot;funktionieren&amp;quot; oder &amp;quot;nicht funktionieren&amp;quot; zu tun. Man sollte sich eher Gedanken um die Genauigkeit und einzuhaltende Maximalfrequenzen bei der Wandlung machen. Der Abschnitt &amp;quot;ADC/Prescaling and Conversion Time&amp;quot; im Datenblatt gibt eigentlich erschoepfend Auskunft darueber. Das &amp;quot;sbi&amp;quot; in dem Beispielcode oben in einem Diskussionsbeitrag zum gcc-tutorial noch auftaucht ist mir schleierhaft, da kann ich mir die Aktualisierung auch sparen. Falls das im Artikel gezeigte Beispiel nachweislich falsch sein sollte: bitte aendern.&lt;br /&gt;
MfG M. Thomas&lt;br /&gt;
&lt;br /&gt;
Butterfly schoen und gut, aber diese beiden Hinweise in letzter Zeit mit der Preisangabe &amp;quot;30Eur&amp;quot;... Ist das irgendwo ein Lager &amp;quot;zu voll&amp;quot; und wird hier versucht &amp;quot;etwas Werbung&amp;quot; zu machen? Der Preis ist &amp;quot;normal&amp;quot; aber wenn man sich umschaut und/oder sich mit ein paar Leuten für eine Sammelbestellung zusammentut gibt es die Teile deutlich guenstiger.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
Einige der Links im Kapitel 11.1.1 (Messen eines Widerstandes) funktionieren nicht. Dies wären:&lt;br /&gt;
* http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c&lt;br /&gt;
* http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h&lt;br /&gt;
* http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile&lt;br /&gt;
&lt;br /&gt;
--[[Benutzer:Oxygene|Oxygene]] 12:05, 12. Nov 2004 (CET)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:AVR-GCC-Tutorial&amp;diff=4766</id>
		<title>Diskussion:AVR-GCC-Tutorial</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:AVR-GCC-Tutorial&amp;diff=4766"/>
		<updated>2004-11-12T11:05:37Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Warum hier im Codebsp. die Frequenz gesetzt wenn man im Einfache Wandlung (Single Conversion)-modus ist?&lt;br /&gt;
&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteile setzen auf 8 (1)&lt;br /&gt;
&lt;br /&gt;
Weil man immer den Vorteiler entsprechend der Quarzfrequenz setzen muß um den ADC nicht mit einem zu hohen Takt zu versorgen. --[[Benutzer:Matthias|Matthias]] 19:58, 21. Sep 2004 (CEST)&lt;br /&gt;
&lt;br /&gt;
Ich benutze folgenden code der funktioniert. Es wird kein vorteiler gesetzt!!&lt;br /&gt;
&lt;br /&gt;
    sbi(ADCSR,ADEN);   //ADC An&lt;br /&gt;
    sbi(ADMUX,MUX0);	//kanal wählen&lt;br /&gt;
    for (;;) {                           /* loop forever */&lt;br /&gt;
           	sbi(ADCSR,ADSC);				//wandlung starten&lt;br /&gt;
    	    	while(bit_is_set(ADCSR,ADSC)); 	//warten bis wandlung fertig&lt;br /&gt;
    		num = ADCW;&lt;br /&gt;
&lt;br /&gt;
--- schnipp ---&lt;br /&gt;
&lt;br /&gt;
Der Vorteiler hat nichts mit &amp;quot;funktionieren&amp;quot; oder &amp;quot;nicht funktionieren&amp;quot; zu tun. Man sollte sich eher Gedanken um die Genauigkeit und einzuhaltende Maximalfrequenzen bei der Wandlung machen. Der Abschnitt &amp;quot;ADC/Prescaling and Conversion Time&amp;quot; im Datenblatt gibt eigentlich erschoepfend Auskunft darueber. Das &amp;quot;sbi&amp;quot; in dem Beispielcode oben in einem Diskussionsbeitrag zum gcc-tutorial noch auftaucht ist mir schleierhaft, da kann ich mir die Aktualisierung auch sparen. Falls das im Artikel gezeigte Beispiel nachweislich falsch sein sollte: bitte aendern.&lt;br /&gt;
MfG M. Thomas&lt;br /&gt;
&lt;br /&gt;
Butterfly schoen und gut, aber diese beiden Hinweise in letzter Zeit mit der Preisangabe &amp;quot;30Eur&amp;quot;... Ist das irgendwo ein Lager &amp;quot;zu voll&amp;quot; und wird hier versucht &amp;quot;etwas Werbung&amp;quot; zu machen? Der Preis ist &amp;quot;normal&amp;quot; aber wenn man sich umschaut und/oder sich mit ein paar Leuten für eine Sammelbestellung zusammentut gibt es die Teile deutlich guenstiger.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
Einige der Links im Kapitel 11.1.1 (Messen eines Widerstandes) funktionieren nicht. Dies wären:&lt;br /&gt;
* http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c&lt;br /&gt;
* http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h&lt;br /&gt;
* http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile&lt;br /&gt;
&lt;br /&gt;
--[[Benutzer:Oxygene|Oxygene]] 12:05, 12. Nov 2004 (CET)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4764</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=4764"/>
		<updated>2004-11-12T10:51:46Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
anstehtm, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt&lt;br /&gt;
:ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global&lt;br /&gt;
:Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt&lt;br /&gt;
:ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global&lt;br /&gt;
:Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer&lt;br /&gt;
:Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues&lt;br /&gt;
:zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt&lt;br /&gt;
:Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART&lt;br /&gt;
:überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende&lt;br /&gt;
:Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART&lt;br /&gt;
:überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende&lt;br /&gt;
:Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen&lt;br /&gt;
:und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches&lt;br /&gt;
:Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von&lt;br /&gt;
:einem 11-Bit Zeichenrahmen:&amp;lt;br /&amp;gt;&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses&lt;br /&gt;
:Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses&lt;br /&gt;
:Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor&lt;br /&gt;
:das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom&lt;br /&gt;
:Empfangs-Schieberegister in das Empfangs-Datenregister transferiert&lt;br /&gt;
:wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister&lt;br /&gt;
:ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres&lt;br /&gt;
:Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation&lt;br /&gt;
:eintreffen. Mit dem Auslesen des Datenregisters wird das Bit&lt;br /&gt;
:automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister&lt;br /&gt;
:befindliche Zeichen vollständig ausgegeben wurde und kein weiteres&lt;br /&gt;
:Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die&lt;br /&gt;
:Kommunikation vollumfänglich abgeschlossen ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das&lt;br /&gt;
:Programm nach dem Senden von Daten auf Empfang schalten muss. Im&lt;br /&gt;
:Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende&lt;br /&gt;
:Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit&lt;br /&gt;
:selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom&lt;br /&gt;
:Sendedatenregister in das Send-Schieberegister übernommen wurde und&lt;br /&gt;
:der UART nun wieder bereit ist, ein neues Zeichen zum Senden&lt;br /&gt;
:aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das&lt;br /&gt;
:Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen&lt;br /&gt;
:Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines&lt;br /&gt;
:empfangenen Zeichens 0 ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen&lt;br /&gt;
:Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im&lt;br /&gt;
:Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das&lt;br /&gt;
:nachfolgende Zeichen komplett empfangen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in&lt;br /&gt;
:das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
UBRR = Taktfrequenz / (Baudrate * 16) - 1&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funktionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen,&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das&lt;br /&gt;
&#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäß unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000;       // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600  // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400           /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE)); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interruptroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039; DAC&#039;&#039;&#039; oder &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann wird durch die Auflösung des &#039;&#039;&#039; ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039; ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/255 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375,&amp;amp;nbsp; 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird entladen (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die&amp;amp;nbsp; Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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 Mass 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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren.&lt;br /&gt;
Wenn das Bit nicht gesetzt ist können die Pin&#039;s wie normale&lt;br /&gt;
I/O-Pins verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden&lt;br /&gt;
Betriebsart muss das Bit gesetzt werden, um die kontinuierliche&lt;br /&gt;
Messung zu aktivieren.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Bit&amp;amp;nbsp; nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal&lt;br /&gt;
gesetzt wird führt der Controller zuerst eine zusätzliche Wandlung&lt;br /&gt;
und erst dann die eigentliche Wandlung aus. Diese zusätzliche&lt;br /&gt;
Wandlung wird zu Initialisierungszwecken durchgeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen&lt;br /&gt;
ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung&lt;br /&gt;
erfolgt ist und geht danach auf 0.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Fr&#039;&#039;&#039;ee Running Select&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird die Betriebsart eingestellt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst&lt;br /&gt;
nun ständig den ausgewählten Kanal und schreibt den gemessenen&lt;br /&gt;
Wert in das &#039;&#039;&#039; ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt wenn eine Umwandlung erfolgt und das&lt;br /&gt;
&#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
wird der &#039;&#039;&#039; ADC Interrupt&#039;&#039;&#039; ausgelöst und die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen..&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht wenn die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch&lt;br /&gt;
gelöscht werden, indem ein logisches 1 in das Register geschrieben&lt;br /&gt;
wird (So steht&#039;s in der AVR-Doku).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister&lt;br /&gt;
&#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&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
ADPS0&#039;&#039;&#039;&lt;br /&gt;
| &#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&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz&lt;br /&gt;
und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der&lt;br /&gt;
CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz&lt;br /&gt;
sein.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorteiler muss also so eingestellt werden, dass die&lt;br /&gt;
CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert&lt;br /&gt;
zwischen 50-200kHz ergibt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmin=CLK/200kHz=4000000/200000=&#039;&#039;&#039;20&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmax=CLK/50kHz=4000000/50000=&#039;&#039;&#039;80&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im&lt;br /&gt;
Interesse der schnelleren Wandlungszeit werden wir hier den Faktor&lt;br /&gt;
32 einstellen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MUX2&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
MUX0&#039;&#039;&#039;&lt;br /&gt;
| Mit diesem 3 Bits wird der zu messende Kanal bestimmt.&lt;br /&gt;
Es wird einfach die entsprechende Pinnummer des Ports&lt;br /&gt;
eingeschrieben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Register beschrieben wird, während dem eine Umwandlung&lt;br /&gt;
läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen&lt;br /&gt;
Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu&lt;br /&gt;
berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Meine Empfehlung ist deswegen klar diese, dass der frei laufende&lt;br /&gt;
Betrieb nur bei einem einzelnen zu verwendenden Analogeingang&lt;br /&gt;
verwendet werden sollte, wenn man sich Probleme bei der Umschalterei&lt;br /&gt;
ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);     //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel; // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);        // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));    // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
	result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);      // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Hinweis:&lt;br /&gt;
| In den folgenden Überlegungen wird als Controller der&lt;br /&gt;
90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen&lt;br /&gt;
AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039; PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039; PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: fix --&amp;gt;&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp&lt;br /&gt;
((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;!-- TODO: fix, irgenwas ist bei der &amp;quot;wiki-konvertierung&amp;quot; wohl schief gelaufen --&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1&amp;lt;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&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;
Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller raus lesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS02&amp;lt;br /&amp;gt;&lt;br /&gt;
CS01&amp;lt;br /&amp;gt;&lt;br /&gt;
CS00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird&lt;br /&gt;
ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang&lt;br /&gt;
geschaltet ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATMega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits&lt;br /&gt;
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert&lt;br /&gt;
(Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
| In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 1 gesetzt.&lt;br /&gt;
Man nennt dies nicht invertierende PWM.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 0 gesetzt.&lt;br /&gt;
Man nennt dies invertierende PWM.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PWM11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler&lt;br /&gt;
(4 CKs) Timer/Counter 1&amp;lt;br /&amp;gt;&lt;br /&gt;
oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1)&lt;br /&gt;
oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input&lt;br /&gt;
Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2 | C-1 | C | 0 | 1 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1,&lt;br /&gt;
C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS12&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein&lt;br /&gt;
Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet&lt;br /&gt;
ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäss Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler&lt;br /&gt;
betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
gespeicherten Wert erreicht wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw.&lt;br /&gt;
gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik&lt;br /&gt;
zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein&lt;br /&gt;
Vergleichswert definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes&lt;br /&gt;
ein Compare Match Interrupt ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt&lt;br /&gt;
ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP)&lt;br /&gt;
auftritt. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein,&lt;br /&gt;
wenn auch ein entsprechender Interrupt ausgelöst werden soll.&amp;lt;font face=&amp;quot;Helvetica&amp;quot; size=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung&lt;br /&gt;
von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters&lt;br /&gt;
von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
übereinstimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist,&lt;br /&gt;
welches anzeigt, dass der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 in das Input Capture Register ICR1 übertragen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei interrupt.h und evtl. signal.h einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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 den Zustand 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). 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4763</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=4763"/>
		<updated>2004-11-12T10:44:20Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
anstehtm, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt&lt;br /&gt;
:ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global&lt;br /&gt;
:Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt&lt;br /&gt;
:ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global&lt;br /&gt;
:Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer&lt;br /&gt;
:Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues&lt;br /&gt;
:zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt&lt;br /&gt;
:Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART&lt;br /&gt;
:überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende&lt;br /&gt;
:Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART&lt;br /&gt;
:überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende&lt;br /&gt;
:Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen&lt;br /&gt;
:und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches&lt;br /&gt;
:Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von&lt;br /&gt;
:einem 11-Bit Zeichenrahmen:&amp;lt;br /&amp;gt;&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses&lt;br /&gt;
:Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses&lt;br /&gt;
:Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor&lt;br /&gt;
:das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom&lt;br /&gt;
:Empfangs-Schieberegister in das Empfangs-Datenregister transferiert&lt;br /&gt;
:wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister&lt;br /&gt;
:ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres&lt;br /&gt;
:Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation&lt;br /&gt;
:eintreffen. Mit dem Auslesen des Datenregisters wird das Bit&lt;br /&gt;
:automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister&lt;br /&gt;
:befindliche Zeichen vollständig ausgegeben wurde und kein weiteres&lt;br /&gt;
:Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die&lt;br /&gt;
:Kommunikation vollumfänglich abgeschlossen ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das&lt;br /&gt;
:Programm nach dem Senden von Daten auf Empfang schalten muss. Im&lt;br /&gt;
:Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende&lt;br /&gt;
:Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit&lt;br /&gt;
:selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom&lt;br /&gt;
:Sendedatenregister in das Send-Schieberegister übernommen wurde und&lt;br /&gt;
:der UART nun wieder bereit ist, ein neues Zeichen zum Senden&lt;br /&gt;
:aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das&lt;br /&gt;
:Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen&lt;br /&gt;
:Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines&lt;br /&gt;
:empfangenen Zeichens 0 ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen&lt;br /&gt;
:Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im&lt;br /&gt;
:Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das&lt;br /&gt;
:nachfolgende Zeichen komplett empfangen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in&lt;br /&gt;
:das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
UBRR = Taktfrequenz / (Baudrate * 16) - 1&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funkstionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter&lt;br /&gt;
Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäss unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000; // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600 // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400   /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;aten &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interrupsroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039; DAC&#039;&#039;&#039; oder &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann wird durch die Auflösung des &#039;&#039;&#039; ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039; ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/255 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375,&amp;amp;nbsp; 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird entladen (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die&amp;amp;nbsp; Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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 Mass 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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren.&lt;br /&gt;
Wenn das Bit nicht gesetzt ist können die Pin&#039;s wie normale&lt;br /&gt;
I/O-Pins verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden&lt;br /&gt;
Betriebsart muss das Bit gesetzt werden, um die kontinuierliche&lt;br /&gt;
Messung zu aktivieren.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Bit&amp;amp;nbsp; nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal&lt;br /&gt;
gesetzt wird führt der Controller zuerst eine zusätzliche Wandlung&lt;br /&gt;
und erst dann die eigentliche Wandlung aus. Diese zusätzliche&lt;br /&gt;
Wandlung wird zu Initialisierungszwecken durchgeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen&lt;br /&gt;
ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung&lt;br /&gt;
erfolgt ist und geht danach auf 0.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Fr&#039;&#039;&#039;ee Running Select&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird die Betriebsart eingestellt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst&lt;br /&gt;
nun ständig den ausgewählten Kanal und schreibt den gemessenen&lt;br /&gt;
Wert in das &#039;&#039;&#039; ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt wenn eine Umwandlung erfolgt und das&lt;br /&gt;
&#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
wird der &#039;&#039;&#039; ADC Interrupt&#039;&#039;&#039; ausgelöst und die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen..&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht wenn die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch&lt;br /&gt;
gelöscht werden, indem ein logisches 1 in das Register geschrieben&lt;br /&gt;
wird (So steht&#039;s in der AVR-Doku).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister&lt;br /&gt;
&#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&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
ADPS0&#039;&#039;&#039;&lt;br /&gt;
| &#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&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz&lt;br /&gt;
und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der&lt;br /&gt;
CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz&lt;br /&gt;
sein.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorteiler muss also so eingestellt werden, dass die&lt;br /&gt;
CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert&lt;br /&gt;
zwischen 50-200kHz ergibt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmin=CLK/200kHz=4000000/200000=&#039;&#039;&#039;20&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmax=CLK/50kHz=4000000/50000=&#039;&#039;&#039;80&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im&lt;br /&gt;
Interesse der schnelleren Wandlungszeit werden wir hier den Faktor&lt;br /&gt;
32 einstellen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MUX2&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
MUX0&#039;&#039;&#039;&lt;br /&gt;
| Mit diesem 3 Bits wird der zu messende Kanal bestimmt.&lt;br /&gt;
Es wird einfach die entsprechende Pinnummer des Ports&lt;br /&gt;
eingeschrieben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Register beschrieben wird, während dem eine Umwandlung&lt;br /&gt;
läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen&lt;br /&gt;
Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu&lt;br /&gt;
berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Meine Empfehlung ist deswegen klar diese, dass der frei laufende&lt;br /&gt;
Betrieb nur bei einem einzelnen zu verwendenden Analogeingang&lt;br /&gt;
verwendet werden sollte, wenn man sich Probleme bei der Umschalterei&lt;br /&gt;
ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);     //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel; // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);        // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));    // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
	result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);      // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Hinweis:&lt;br /&gt;
| In den folgenden Überlegungen wird als Controller der&lt;br /&gt;
90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen&lt;br /&gt;
AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039; PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039; PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: fix --&amp;gt;&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp&lt;br /&gt;
((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;!-- TODO: fix, irgenwas ist bei der &amp;quot;wiki-konvertierung&amp;quot; wohl schief gelaufen --&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1&amp;lt;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&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;
Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller raus lesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS02&amp;lt;br /&amp;gt;&lt;br /&gt;
CS01&amp;lt;br /&amp;gt;&lt;br /&gt;
CS00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird&lt;br /&gt;
ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang&lt;br /&gt;
geschaltet ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATMega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits&lt;br /&gt;
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert&lt;br /&gt;
(Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
| In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 1 gesetzt.&lt;br /&gt;
Man nennt dies nicht invertierende PWM.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 0 gesetzt.&lt;br /&gt;
Man nennt dies invertierende PWM.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PWM11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler&lt;br /&gt;
(4 CKs) Timer/Counter 1&amp;lt;br /&amp;gt;&lt;br /&gt;
oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1)&lt;br /&gt;
oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input&lt;br /&gt;
Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2 | C-1 | C | 0 | 1 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1,&lt;br /&gt;
C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS12&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein&lt;br /&gt;
Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet&lt;br /&gt;
ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäss Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler&lt;br /&gt;
betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
gespeicherten Wert erreicht wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw.&lt;br /&gt;
gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik&lt;br /&gt;
zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein&lt;br /&gt;
Vergleichswert definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes&lt;br /&gt;
ein Compare Match Interrupt ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt&lt;br /&gt;
ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP)&lt;br /&gt;
auftritt. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein,&lt;br /&gt;
wenn auch ein entsprechender Interrupt ausgelöst werden soll.&amp;lt;font face=&amp;quot;Helvetica&amp;quot; size=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung&lt;br /&gt;
von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters&lt;br /&gt;
von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
übereinstimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist,&lt;br /&gt;
welches anzeigt, dass der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 in das Input Capture Register ICR1 übertragen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei interrupt.h und evtl. signal.h einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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 den Zustand 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). 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4762</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=4762"/>
		<updated>2004-11-12T10:29:09Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: Tabellen vereinfacht&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
anstehtm, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt&lt;br /&gt;
:ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global&lt;br /&gt;
:Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt&lt;br /&gt;
:ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global&lt;br /&gt;
:Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer&lt;br /&gt;
:Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues&lt;br /&gt;
:zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt&lt;br /&gt;
:Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART&lt;br /&gt;
:überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende&lt;br /&gt;
:Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART&lt;br /&gt;
:überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende&lt;br /&gt;
:Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHR9&#039;&#039;&#039; (9 Bit Characters)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, können 9 Bit lange Zeichen übertragen&lt;br /&gt;
:und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches&lt;br /&gt;
:Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von&lt;br /&gt;
:einem 11-Bit Zeichenrahmen:&amp;lt;br /&amp;gt;&lt;br /&gt;
:1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses&lt;br /&gt;
:Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses&lt;br /&gt;
:Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor&lt;br /&gt;
:das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom&lt;br /&gt;
:Empfangs-Schieberegister in das Empfangs-Datenregister transferiert&lt;br /&gt;
:wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister&lt;br /&gt;
:ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres&lt;br /&gt;
:Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation&lt;br /&gt;
:eintreffen. Mit dem Auslesen des Datenregisters wird das Bit&lt;br /&gt;
:automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister&lt;br /&gt;
:befindliche Zeichen vollständig ausgegeben wurde und kein weiteres&lt;br /&gt;
:Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die&lt;br /&gt;
:Kommunikation vollumfänglich abgeschlossen ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das&lt;br /&gt;
:Programm nach dem Senden von Daten auf Empfang schalten muss. Im&lt;br /&gt;
:Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende&lt;br /&gt;
:Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit&lt;br /&gt;
:selber löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom&lt;br /&gt;
:Sendedatenregister in das Send-Schieberegister übernommen wurde und&lt;br /&gt;
:der UART nun wieder bereit ist, ein neues Zeichen zum Senden&lt;br /&gt;
:aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das&lt;br /&gt;
:Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen&lt;br /&gt;
:Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines&lt;br /&gt;
:empfangenen Zeichens 0 ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen&lt;br /&gt;
:Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OR&#039;&#039;&#039; (&#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un)&amp;lt;br /&amp;gt;&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im&lt;br /&gt;
:Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das&lt;br /&gt;
:nachfolgende Zeichen komplett empfangen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&amp;lt;br /&amp;gt;&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in&lt;br /&gt;
:das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
UBRR = Taktfrequenz / (Baudrate * 16) - 1&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART so ist oft eine fehlerhafte Einstellung Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funkstionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter&lt;br /&gt;
Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäss unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000; // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600 // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400   /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;aten &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interrupsroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039; DAC&#039;&#039;&#039; oder &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann wird durch die Auflösung des &#039;&#039;&#039; ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039; ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/255 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375,&amp;amp;nbsp; 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird entladen (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die&amp;amp;nbsp; Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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 Mass 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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren.&lt;br /&gt;
Wenn das Bit nicht gesetzt ist können die Pin&#039;s wie normale&lt;br /&gt;
I/O-Pins verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden&lt;br /&gt;
Betriebsart muss das Bit gesetzt werden, um die kontinuierliche&lt;br /&gt;
Messung zu aktivieren.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Bit&amp;amp;nbsp; nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal&lt;br /&gt;
gesetzt wird führt der Controller zuerst eine zusätzliche Wandlung&lt;br /&gt;
und erst dann die eigentliche Wandlung aus. Diese zusätzliche&lt;br /&gt;
Wandlung wird zu Initialisierungszwecken durchgeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen&lt;br /&gt;
ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung&lt;br /&gt;
erfolgt ist und geht danach auf 0.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Fr&#039;&#039;&#039;ee Running Select&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird die Betriebsart eingestellt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst&lt;br /&gt;
nun ständig den ausgewählten Kanal und schreibt den gemessenen&lt;br /&gt;
Wert in das &#039;&#039;&#039; ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt wenn eine Umwandlung erfolgt und das&lt;br /&gt;
&#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
wird der &#039;&#039;&#039; ADC Interrupt&#039;&#039;&#039; ausgelöst und die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen..&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht wenn die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch&lt;br /&gt;
gelöscht werden, indem ein logisches 1 in das Register geschrieben&lt;br /&gt;
wird (So steht&#039;s in der AVR-Doku).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister&lt;br /&gt;
&#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&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
ADPS0&#039;&#039;&#039;&lt;br /&gt;
| &#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&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz&lt;br /&gt;
und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der&lt;br /&gt;
CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz&lt;br /&gt;
sein.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorteiler muss also so eingestellt werden, dass die&lt;br /&gt;
CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert&lt;br /&gt;
zwischen 50-200kHz ergibt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmin=CLK/200kHz=4000000/200000=&#039;&#039;&#039;20&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmax=CLK/50kHz=4000000/50000=&#039;&#039;&#039;80&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im&lt;br /&gt;
Interesse der schnelleren Wandlungszeit werden wir hier den Faktor&lt;br /&gt;
32 einstellen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MUX2&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
MUX0&#039;&#039;&#039;&lt;br /&gt;
| Mit diesem 3 Bits wird der zu messende Kanal bestimmt.&lt;br /&gt;
Es wird einfach die entsprechende Pinnummer des Ports&lt;br /&gt;
eingeschrieben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Register beschrieben wird, während dem eine Umwandlung&lt;br /&gt;
läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen&lt;br /&gt;
Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu&lt;br /&gt;
berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Meine Empfehlung ist deswegen klar diese, dass der frei laufende&lt;br /&gt;
Betrieb nur bei einem einzelnen zu verwendenden Analogeingang&lt;br /&gt;
verwendet werden sollte, wenn man sich Probleme bei der Umschalterei&lt;br /&gt;
ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);     //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel; // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);        // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));    // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
	result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);      // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Hinweis:&lt;br /&gt;
| In den folgenden Überlegungen wird als Controller der&lt;br /&gt;
90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen&lt;br /&gt;
AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039; PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039; PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: fix --&amp;gt;&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp&lt;br /&gt;
((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;!-- TODO: fix, irgenwas ist bei der &amp;quot;wiki-konvertierung&amp;quot; wohl schief gelaufen --&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1&amp;lt;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&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;
Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller raus lesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS02&amp;lt;br /&amp;gt;&lt;br /&gt;
CS01&amp;lt;br /&amp;gt;&lt;br /&gt;
CS00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird&lt;br /&gt;
ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang&lt;br /&gt;
geschaltet ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATMega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits&lt;br /&gt;
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert&lt;br /&gt;
(Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
| In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 1 gesetzt.&lt;br /&gt;
Man nennt dies nicht invertierende PWM.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 0 gesetzt.&lt;br /&gt;
Man nennt dies invertierende PWM.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PWM11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler&lt;br /&gt;
(4 CKs) Timer/Counter 1&amp;lt;br /&amp;gt;&lt;br /&gt;
oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1)&lt;br /&gt;
oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input&lt;br /&gt;
Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2 | C-1 | C | 0 | 1 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1,&lt;br /&gt;
C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS12&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein&lt;br /&gt;
Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet&lt;br /&gt;
ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäss Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler&lt;br /&gt;
betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
gespeicherten Wert erreicht wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw.&lt;br /&gt;
gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik&lt;br /&gt;
zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein&lt;br /&gt;
Vergleichswert definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes&lt;br /&gt;
ein Compare Match Interrupt ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt&lt;br /&gt;
ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP)&lt;br /&gt;
auftritt. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein,&lt;br /&gt;
wenn auch ein entsprechender Interrupt ausgelöst werden soll.&amp;lt;font face=&amp;quot;Helvetica&amp;quot; size=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung&lt;br /&gt;
von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters&lt;br /&gt;
von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
übereinstimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist,&lt;br /&gt;
welches anzeigt, dass der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 in das Input Capture Register ICR1 übertragen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei interrupt.h und evtl. signal.h einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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 den Zustand 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). 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4761</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=4761"/>
		<updated>2004-11-12T09:55:41Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: Absatzformatierung&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen den Eingangspin des Controllers und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die Versorgungsspannung und Masse geschaltet.&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
anstehtm, wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehr als zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART RX Complete Interrupt&lt;br /&gt;
ausgelöst wenn ein Zeichen vom UART empfangen wurde. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART TX Complete Interrupt&lt;br /&gt;
ausgelöst wenn ein Zeichen vom UART gesendet wurde. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART Datenregister Leer&lt;br /&gt;
Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues&lt;br /&gt;
zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt&lt;br /&gt;
Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Nur wenn dieses Bit gesetzt ist arbeitet der Empfänger des UART&lt;br /&gt;
überhaupt. Wenn das Bit nicht gesetzt ist kann der entsprechende&lt;br /&gt;
Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Nur wenn dieses Bit gesetzt ist arbeitet der Sender des UART&lt;br /&gt;
überhaupt. Wenn das Bit nicht gesetzt ist kann der entsprechende&lt;br /&gt;
Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| 9 Bit Characters&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist können 9 Bit lange Zeichen übertragen&lt;br /&gt;
und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches&lt;br /&gt;
Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von&lt;br /&gt;
einem 11-Bit Zeichenrahmen:&amp;lt;br /&amp;gt;&lt;br /&gt;
1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| Receive Data Bit 8&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses&lt;br /&gt;
Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
| Transmit Data Bit 8&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses&lt;br /&gt;
Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor&lt;br /&gt;
das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;amp;amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| UART Receive Complete&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom&lt;br /&gt;
Empfangs-Schieberegister in das Empfangs-Datenregister transferiert&lt;br /&gt;
wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Zeichen muss nun schnellstmöglich aus dem Datenregister&lt;br /&gt;
ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres&lt;br /&gt;
Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation&lt;br /&gt;
eintreffen. Mit dem Auslesen des Datenregisters wird das Bit&lt;br /&gt;
automatisch gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| UART Transmit Complete&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister&lt;br /&gt;
befindliche Zeichen vollständig ausgegeben wurde und kein weiteres&lt;br /&gt;
Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die&lt;br /&gt;
Kommunikation vollumfänglich abgeschlossen ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das&lt;br /&gt;
Programm nach dem Senden von Daten auf Empfang schalten muss. Im&lt;br /&gt;
Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit nur dann automatisch gelöscht, wenn der entsprechende&lt;br /&gt;
Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit&lt;br /&gt;
selber löschen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom&lt;br /&gt;
Sendedatenregister in das Send-Schieberegister übernommen wurde und&lt;br /&gt;
der UART nun wieder bereit ist, ein neues Zeichen zum Senden&lt;br /&gt;
aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn ein Zeichen in das&lt;br /&gt;
Sendedatenregister geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn der UART einen&lt;br /&gt;
Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines&lt;br /&gt;
empfangenen Zeichens 0 ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen&lt;br /&gt;
Zeichens 1 ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im&lt;br /&gt;
Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das&lt;br /&gt;
nachfolgende Zeichen komplett empfangen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das nachfolgende Zeichen wird verworfen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in&lt;br /&gt;
das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
UBRR = Taktfrequenz / (Baudrate * 16) - 1&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART so ist oft eine fehlerhafte Einstellung Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funkstionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter&lt;br /&gt;
Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäss unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000; // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600 // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400   /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;aten &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interrupsroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039; DAC&#039;&#039;&#039; oder &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann wird durch die Auflösung des &#039;&#039;&#039; ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039; ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/255 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375,&amp;amp;nbsp; 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird entladen (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die&amp;amp;nbsp; Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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 Mass 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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren.&lt;br /&gt;
Wenn das Bit nicht gesetzt ist können die Pin&#039;s wie normale&lt;br /&gt;
I/O-Pins verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden&lt;br /&gt;
Betriebsart muss das Bit gesetzt werden, um die kontinuierliche&lt;br /&gt;
Messung zu aktivieren.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Bit&amp;amp;nbsp; nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal&lt;br /&gt;
gesetzt wird führt der Controller zuerst eine zusätzliche Wandlung&lt;br /&gt;
und erst dann die eigentliche Wandlung aus. Diese zusätzliche&lt;br /&gt;
Wandlung wird zu Initialisierungszwecken durchgeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen&lt;br /&gt;
ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung&lt;br /&gt;
erfolgt ist und geht danach auf 0.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Fr&#039;&#039;&#039;ee Running Select&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird die Betriebsart eingestellt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst&lt;br /&gt;
nun ständig den ausgewählten Kanal und schreibt den gemessenen&lt;br /&gt;
Wert in das &#039;&#039;&#039; ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt wenn eine Umwandlung erfolgt und das&lt;br /&gt;
&#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
wird der &#039;&#039;&#039; ADC Interrupt&#039;&#039;&#039; ausgelöst und die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen..&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht wenn die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch&lt;br /&gt;
gelöscht werden, indem ein logisches 1 in das Register geschrieben&lt;br /&gt;
wird (So steht&#039;s in der AVR-Doku).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister&lt;br /&gt;
&#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&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
ADPS0&#039;&#039;&#039;&lt;br /&gt;
| &#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&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz&lt;br /&gt;
und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der&lt;br /&gt;
CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz&lt;br /&gt;
sein.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorteiler muss also so eingestellt werden, dass die&lt;br /&gt;
CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert&lt;br /&gt;
zwischen 50-200kHz ergibt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmin=CLK/200kHz=4000000/200000=&#039;&#039;&#039;20&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmax=CLK/50kHz=4000000/50000=&#039;&#039;&#039;80&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im&lt;br /&gt;
Interesse der schnelleren Wandlungszeit werden wir hier den Faktor&lt;br /&gt;
32 einstellen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MUX2&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
MUX0&#039;&#039;&#039;&lt;br /&gt;
| Mit diesem 3 Bits wird der zu messende Kanal bestimmt.&lt;br /&gt;
Es wird einfach die entsprechende Pinnummer des Ports&lt;br /&gt;
eingeschrieben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Register beschrieben wird, während dem eine Umwandlung&lt;br /&gt;
läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen&lt;br /&gt;
Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu&lt;br /&gt;
berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Meine Empfehlung ist deswegen klar diese, dass der frei laufende&lt;br /&gt;
Betrieb nur bei einem einzelnen zu verwendenden Analogeingang&lt;br /&gt;
verwendet werden sollte, wenn man sich Probleme bei der Umschalterei&lt;br /&gt;
ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);     //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel; // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);        // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));    // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
	result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);      // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Hinweis:&lt;br /&gt;
| In den folgenden Überlegungen wird als Controller der&lt;br /&gt;
90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen&lt;br /&gt;
AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039; PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039; PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: fix --&amp;gt;&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp&lt;br /&gt;
((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;!-- TODO: fix, irgenwas ist bei der &amp;quot;wiki-konvertierung&amp;quot; wohl schief gelaufen --&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1&amp;lt;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&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;
Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller raus lesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS02&amp;lt;br /&amp;gt;&lt;br /&gt;
CS01&amp;lt;br /&amp;gt;&lt;br /&gt;
CS00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird&lt;br /&gt;
ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang&lt;br /&gt;
geschaltet ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATMega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits&lt;br /&gt;
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert&lt;br /&gt;
(Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
| In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 1 gesetzt.&lt;br /&gt;
Man nennt dies nicht invertierende PWM.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 0 gesetzt.&lt;br /&gt;
Man nennt dies invertierende PWM.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PWM11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler&lt;br /&gt;
(4 CKs) Timer/Counter 1&amp;lt;br /&amp;gt;&lt;br /&gt;
oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1)&lt;br /&gt;
oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input&lt;br /&gt;
Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2 | C-1 | C | 0 | 1 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1,&lt;br /&gt;
C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS12&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein&lt;br /&gt;
Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet&lt;br /&gt;
ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäss Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler&lt;br /&gt;
betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
gespeicherten Wert erreicht wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw.&lt;br /&gt;
gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik&lt;br /&gt;
zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein&lt;br /&gt;
Vergleichswert definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes&lt;br /&gt;
ein Compare Match Interrupt ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt&lt;br /&gt;
ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP)&lt;br /&gt;
auftritt. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein,&lt;br /&gt;
wenn auch ein entsprechender Interrupt ausgelöst werden soll.&amp;lt;font face=&amp;quot;Helvetica&amp;quot; size=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung&lt;br /&gt;
von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters&lt;br /&gt;
von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
übereinstimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist,&lt;br /&gt;
welches anzeigt, dass der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 in das Input Capture Register ICR1 übertragen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei interrupt.h und evtl. signal.h einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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 den Zustand 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). 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4760</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=4760"/>
		<updated>2004-11-12T09:32:45Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Exkurs: makefiles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers, der vom gcc Compiler untersützt wird (alle ATmegas und die meisten AT90). Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. Eine brauchbare Testplattform ist auch der [[AVR Butterfly]].&lt;br /&gt;
* AVRGCC-Compiler. Kostenlos erhältlich für nahezu alle Plattformen/Betriebssysteme. Für MS-Windows im Packet [[WinAVR]]; für Unix/Linx siehe auch Hinweise im Artikel [[AVR-GCC]].&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
* Nicht unbedingt erforderlich aber zur Simulation und zum Debuggen unter MS-Windows recht nützlich: [[AVR-Studio]] (siehe Abschnitt Exkurs: makefiles).&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;Makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Aufrufe bereits im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingefügt.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
seltener sind folgende Einstellungen durchzuführen:&lt;br /&gt;
* Grad der Optimierung&lt;br /&gt;
* Methode zur Erzeugung der Debug-Symbole (Debug-Format)&lt;br /&gt;
* Assembler-Quellcode-Dateien (S-Dateien)&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Ausschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[AVRDUDE]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU entsprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien, vgl. [[Include-Files (C)]]) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon in der SRC-Liste enthalten. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware [[AVRDUDE]] angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Sonstige Einstellungen ==&lt;br /&gt;
&lt;br /&gt;
=== Optimierungsgrad ===&lt;br /&gt;
&lt;br /&gt;
Der gcc-Compiler kennt verschiedene Stufen der Optimierung. Nur zu Testzwecken sollte die Optimierung ganz deaktiviert werden (&#039;&#039;OPT = 0&#039;&#039;). Die weiteren möglichen Optionen weisen den Compiler an, möglichst kompakten oder möglichst schnellen Code zu erzeugen. In den weitaus meisten Fällen ist &#039;&#039;OPT = s&#039;&#039; die optimale (sic) Einstellung, damit wird kompakter und oft auch der &amp;quot;schnellste&amp;quot; Maschienencode erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Debug-Format ===&lt;br /&gt;
&lt;br /&gt;
Unterstützt werden die Formate stabs und dwarf-2. Das Format wir hinter &#039;&#039;DEBUG =&#039;&#039; eingestellt. Siehe dazu Abschnitt &#039;&#039;Eingabedateien zur Simulation&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Dateien ===&lt;br /&gt;
&lt;br /&gt;
Die im Projekt genutzten Assembler-Dateien werden hinter ASRC durch Leerzeichen getrennt aufgelistet. Assembler-Dateien haben immer die Endung .S (grosses S). Ist zum Beispiel der Assembler-Quellcode eines Software-UARTs in einer Datei softuart.S enthalten lautet die Zeile: &#039;&#039;ASRC = softuart.S&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.356) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
Statt des Software-Simulators kann das AVR-Studio auch genutzt werden, um mit dem ATMEL JTAGICE, ein Nachbau davon (BootICE, Evertool o.ä.) oder dem ATMEL JTAGICE MKII &amp;quot;im System&amp;quot; zu debuggen. Dazu sind keine speziellen Einstellungen im makefile erforderlich. Debugging bzw. &amp;quot;In-System-Emulation&amp;quot; mit dem JTAGICE und JTAGICE MKII sind in der AVR-Studio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 18446744073709551615.&lt;br /&gt;
&lt;br /&gt;
Bei allen vorgenannten Datentypen ist zu beachten, dass die Bezeichnungen für &amp;quot;Worte&amp;quot; teilweise je nach Prozessorplattform unterschiedlich verwendet werden. Die angegebenen Definitionen beziehen sich auf die im Zusammenhang mit AVR/8-bit-Controllern üblichen &amp;quot;Bit-Breiten&amp;quot; (In Erläuterungen zum ARM7TDMI z.B. werden oft 32-bit Integer mit &amp;quot;Wort&amp;quot; ohne weitere Ergänzung bezeichnet). Es empfiehlt sich daher, die im folgenden Abschnitt beschriebenen standardisierten Datentypen zu nutzen und damit &amp;quot;Missverständnissen&amp;quot; vorzubeugen, die z.B. bei der Portierung von C-Code zwischen verschiedenen Plattformen auftreten können.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Integer(Ganzzahl)-Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-[[Word|Wort]] (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein [[Byte]] (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
Die Typen ohne vorangestelltes &#039;&#039;u&#039;&#039; können auch vorzeichenbehaftete Zahlen speichern. &#039;&#039;int8_t&#039;&#039; geht also von -128 bis 127, &#039;&#039;uint8_t&#039;&#039; dagegen geht von 0 bis 255.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen&lt;br /&gt;
den Eingangspin des Controllers und Masse geschaltet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die&lt;br /&gt;
Versorgungsspannung und Masse geschaltet.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten,&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehr als zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART RX Complete Interrupt&lt;br /&gt;
ausgelöst wenn ein Zeichen vom UART empfangen wurde. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART TX Complete Interrupt&lt;br /&gt;
ausgelöst wenn ein Zeichen vom UART gesendet wurde. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART Datenregister Leer&lt;br /&gt;
Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues&lt;br /&gt;
zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt&lt;br /&gt;
Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Nur wenn dieses Bit gesetzt ist arbeitet der Empfänger des UART&lt;br /&gt;
überhaupt. Wenn das Bit nicht gesetzt ist kann der entsprechende&lt;br /&gt;
Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Nur wenn dieses Bit gesetzt ist arbeitet der Sender des UART&lt;br /&gt;
überhaupt. Wenn das Bit nicht gesetzt ist kann der entsprechende&lt;br /&gt;
Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| 9 Bit Characters&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist können 9 Bit lange Zeichen übertragen&lt;br /&gt;
und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches&lt;br /&gt;
Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von&lt;br /&gt;
einem 11-Bit Zeichenrahmen:&amp;lt;br /&amp;gt;&lt;br /&gt;
1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| Receive Data Bit 8&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses&lt;br /&gt;
Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
| Transmit Data Bit 8&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses&lt;br /&gt;
Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor&lt;br /&gt;
das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;amp;amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| UART Receive Complete&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom&lt;br /&gt;
Empfangs-Schieberegister in das Empfangs-Datenregister transferiert&lt;br /&gt;
wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Zeichen muss nun schnellstmöglich aus dem Datenregister&lt;br /&gt;
ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres&lt;br /&gt;
Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation&lt;br /&gt;
eintreffen. Mit dem Auslesen des Datenregisters wird das Bit&lt;br /&gt;
automatisch gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| UART Transmit Complete&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister&lt;br /&gt;
befindliche Zeichen vollständig ausgegeben wurde und kein weiteres&lt;br /&gt;
Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die&lt;br /&gt;
Kommunikation vollumfänglich abgeschlossen ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das&lt;br /&gt;
Programm nach dem Senden von Daten auf Empfang schalten muss. Im&lt;br /&gt;
Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit nur dann automatisch gelöscht, wenn der entsprechende&lt;br /&gt;
Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit&lt;br /&gt;
selber löschen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom&lt;br /&gt;
Sendedatenregister in das Send-Schieberegister übernommen wurde und&lt;br /&gt;
der UART nun wieder bereit ist, ein neues Zeichen zum Senden&lt;br /&gt;
aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn ein Zeichen in das&lt;br /&gt;
Sendedatenregister geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn der UART einen&lt;br /&gt;
Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines&lt;br /&gt;
empfangenen Zeichens 0 ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen&lt;br /&gt;
Zeichens 1 ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im&lt;br /&gt;
Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das&lt;br /&gt;
nachfolgende Zeichen komplett empfangen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das nachfolgende Zeichen wird verworfen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in&lt;br /&gt;
das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
UBRR = Taktfrequenz / (Baudrate * 16) - 1&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART so ist oft eine fehlerhafte Einstellung Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfielt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funkstionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter&lt;br /&gt;
Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Atmega16 hat mehrere Konfigurationsregister für USART und erfordert eine etwas andere Konfiguration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);			//UART TX einschalten&lt;br /&gt;
  UCSRC |= (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0);	//Asynchron 8N1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäss unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init beim AT90S2313 */&lt;br /&gt;
#define F_CPU 4000000; // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600 // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wieder für den Mega16 mit einem 16bit-Register eine andere Programmierung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
  /* USART-Init beim ATmegaXX */&lt;br /&gt;
  #define F_OSC 3686400   /* Oszillator-Frequenz in Hz */&lt;br /&gt;
  #define UART_BAUD_RATE 9600&lt;br /&gt;
  #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)&lt;br /&gt;
&lt;br /&gt;
  UBRRH=(uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)&amp;gt;&amp;gt;8);&lt;br /&gt;
  UBRRL=(uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&lt;br /&gt;
  /* alternativ bei der avr-libc &amp;quot;direkt 16bit&amp;quot; : */&lt;br /&gt;
  UBRR=UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;aten &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
while (USR &amp;amp; (1&amp;lt;&amp;lt;UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Es ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&amp;lt;!-- Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde. MT: oben allgemeiner Formuliert wg. UART&amp;lt;-&amp;gt;USART) --&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf) {&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE); /* warten bis Senden moeglich */&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     szBuf++;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Warteschleifen sind insofern etwas kritisch, da während des Sendens eines&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. emfangenen Zeichen/Bytes zwischengespeichert und mittels Interrupsroutinen an den U(S)ART weitergebgen bzw. ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&amp;lt;!--Dies wird aber ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen. Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039; DAC&#039;&#039;&#039; oder &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann wird durch die Auflösung des &#039;&#039;&#039; ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039; ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/255 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375,&amp;amp;nbsp; 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...&amp;lt;0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...&amp;lt;1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...&amp;lt;1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...&amp;lt;2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...&amp;lt;3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...&amp;lt;3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...&amp;lt;4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird entladen (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die&amp;amp;nbsp; Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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 Mass 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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren.&lt;br /&gt;
Wenn das Bit nicht gesetzt ist können die Pin&#039;s wie normale&lt;br /&gt;
I/O-Pins verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden&lt;br /&gt;
Betriebsart muss das Bit gesetzt werden, um die kontinuierliche&lt;br /&gt;
Messung zu aktivieren.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Bit&amp;amp;nbsp; nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal&lt;br /&gt;
gesetzt wird führt der Controller zuerst eine zusätzliche Wandlung&lt;br /&gt;
und erst dann die eigentliche Wandlung aus. Diese zusätzliche&lt;br /&gt;
Wandlung wird zu Initialisierungszwecken durchgeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen&lt;br /&gt;
ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung&lt;br /&gt;
erfolgt ist und geht danach auf 0.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Fr&#039;&#039;&#039;ee Running Select&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird die Betriebsart eingestellt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst&lt;br /&gt;
nun ständig den ausgewählten Kanal und schreibt den gemessenen&lt;br /&gt;
Wert in das &#039;&#039;&#039; ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt wenn eine Umwandlung erfolgt und das&lt;br /&gt;
&#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
wird der &#039;&#039;&#039; ADC Interrupt&#039;&#039;&#039; ausgelöst und die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen..&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht wenn die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch&lt;br /&gt;
gelöscht werden, indem ein logisches 1 in das Register geschrieben&lt;br /&gt;
wird (So steht&#039;s in der AVR-Doku).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister&lt;br /&gt;
&#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&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
ADPS0&#039;&#039;&#039;&lt;br /&gt;
| &#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&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz&lt;br /&gt;
und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der&lt;br /&gt;
CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz&lt;br /&gt;
sein.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorteiler muss also so eingestellt werden, dass die&lt;br /&gt;
CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert&lt;br /&gt;
zwischen 50-200kHz ergibt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmin=CLK/200kHz=4000000/200000=&#039;&#039;&#039;20&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmax=CLK/50kHz=4000000/50000=&#039;&#039;&#039;80&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im&lt;br /&gt;
Interesse der schnelleren Wandlungszeit werden wir hier den Faktor&lt;br /&gt;
32 einstellen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MUX2&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
MUX0&#039;&#039;&#039;&lt;br /&gt;
| Mit diesem 3 Bits wird der zu messende Kanal bestimmt.&lt;br /&gt;
Es wird einfach die entsprechende Pinnummer des Ports&lt;br /&gt;
eingeschrieben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Register beschrieben wird, während dem eine Umwandlung&lt;br /&gt;
läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen&lt;br /&gt;
Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu&lt;br /&gt;
berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Meine Empfehlung ist deswegen klar diese, dass der frei laufende&lt;br /&gt;
Betrieb nur bei einem einzelnen zu verwendenden Analogeingang&lt;br /&gt;
verwendet werden sollte, wenn man sich Probleme bei der Umschalterei&lt;br /&gt;
ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);     //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel; // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);        // eine ADC-Wandlung &lt;br /&gt;
  &amp;lt;!--while(!(ADCSRA &amp;amp; 0x10));    // auf Abschluss der Konvertierung warten (ADIF-bit) --&amp;gt;&lt;br /&gt;
  while(!(ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADIF)));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
	result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);      // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Hinweis:&lt;br /&gt;
| In den folgenden Überlegungen wird als Controller der&lt;br /&gt;
90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen&lt;br /&gt;
AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039; PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039; PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: fix --&amp;gt;&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp&lt;br /&gt;
((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;!-- TODO: fix, irgenwas ist bei der &amp;quot;wiki-konvertierung&amp;quot; wohl schief gelaufen --&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1&amp;lt;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&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;
Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller raus lesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS02&amp;lt;br /&amp;gt;&lt;br /&gt;
CS01&amp;lt;br /&amp;gt;&lt;br /&gt;
CS00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird&lt;br /&gt;
ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang&lt;br /&gt;
geschaltet ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATMega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits&lt;br /&gt;
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert&lt;br /&gt;
(Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
| In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 1 gesetzt.&lt;br /&gt;
Man nennt dies nicht invertierende PWM.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 0 gesetzt.&lt;br /&gt;
Man nennt dies invertierende PWM.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PWM11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler&lt;br /&gt;
(4 CKs) Timer/Counter 1&amp;lt;br /&amp;gt;&lt;br /&gt;
oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1)&lt;br /&gt;
oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input&lt;br /&gt;
Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2 | C-1 | C | 0 | 1 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1,&lt;br /&gt;
C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS12&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein&lt;br /&gt;
Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet&lt;br /&gt;
ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäss Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler&lt;br /&gt;
betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
gespeicherten Wert erreicht wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw.&lt;br /&gt;
gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik&lt;br /&gt;
zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein&lt;br /&gt;
Vergleichswert definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes&lt;br /&gt;
ein Compare Match Interrupt ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt&lt;br /&gt;
ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP)&lt;br /&gt;
auftritt. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein,&lt;br /&gt;
wenn auch ein entsprechender Interrupt ausgelöst werden soll.&amp;lt;font face=&amp;quot;Helvetica&amp;quot; size=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung&lt;br /&gt;
von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters&lt;br /&gt;
von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
übereinstimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist,&lt;br /&gt;
welches anzeigt, dass der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 in das Input Capture Register ICR1 übertragen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei interrupt.h und evtl. signal.h einbinden mittels:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// fuer sei(), cli() und INTERRUPT():&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// fuer SIGNAL() auch:&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&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. Dazu gibt es zwei Definitionen: &#039;&#039;&#039;SIGNAL&#039;&#039;&#039; und &#039;&#039;&#039;INTERRUPT&#039;&#039;&#039;, welche allerdings AVR-GCC spezifisch sind und bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
=== SIGNAL ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;SIGNAL&#039;&#039; wird eine Funktion für die Bearbeitung eines Interrupts eingeleitet. Als Argument muss dabei die Benennung des entsprechenden Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien IOxxxx.h zu finden. Vor Nutzung von SIGNAL muss die Header-Datei signal.h eingebunden werden. Mögliche Funktionsrümpfe für solche Interruptfunktionen sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des 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;
=== INTERRUPT ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und sollte wirklich &#039;&#039;&#039;nur dann&#039;&#039;&#039; angewendet werden, wenn man sich absolut sicher ist, das Ganze auch im Griff zu haben. Vor Nutzung von INTERRUPT muss die Header-Datei interrupt.h eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Interruptroutinen (ISRs) sollten 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 den Zustand 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). 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;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aber Achtung: Ersetzt man zum Beispiel&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash[] PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
const char textImFlash* PROGMEM = &amp;quot;Pusemuckel&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dann kann es zu Problemen mit AVR-GCC kommen. Zu erkennen daran, dass der textImFlash zu den Konstanten ans Ende des Programmcodes gelegt wird, von dem aus er zur Benutzung eigentlich ins RAM kopiert werden sollte. Da der lesende Code trotzdem an einer Stelle vorne im Flash sucht, wird Unsinn gelesen. Dies scheint ein Bug des AVR-GCC (gesehen bei 3.4.1) zu sein.&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer [[C-Präprozessor]]-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
= Assembler und Inline-Assembler =&lt;br /&gt;
&lt;br /&gt;
Gelegentlich erweist es sich als nützlich, C und Assembler-Code in einer Anwendung zu nutzen. Typischerweise wird das Hauptprogramm in C verfasst und wenige, extrem zeitkritische oder hardwarenahe Operationen in Assembler.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;gcc-Toolchain&amp;quot; bietet dazu zwei Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
;Inline-Assembler: &lt;br /&gt;
Die Assembleranweisungen werden in direkt in den C-Code integriert. Eine Quellcode-Datei enhält somit C- und Assembleranweisungen&lt;br /&gt;
&lt;br /&gt;
;Assembler-Dateien: &lt;br /&gt;
Der Assembler-Codecode befindet sich in eigenen Quellcodedateien. Dieser werden vom gnu-Assembler (avr-as) zu Object-Dateien assembliert (&amp;quot;compiliert&amp;quot;) und mit den aus dem C-Code erstellten Object-Dateien zusammengebunden (gelinkt).&lt;br /&gt;
&lt;br /&gt;
== Inline-Assembler ==&lt;br /&gt;
&lt;br /&gt;
Inline-Assembler bietet sich an, wenn nur wenig Assembleranweisungen benötigt werden. Typische Anwendung sind kurze Codesequenzen für zeitkritische Operationen in Interrupt-Routinen oder sehr präzise Warteschleifen (z.B. 1-Wire). Inline-Assembler wird mit &#039;&#039;&#039;asm volatile&#039;&#039;&#039; eingeleitet, die Assembler-Anweisungen werden in einer Zeichenkette zusammengefasst, die als &amp;quot;Parameter&amp;quot; übergeben wird. Durch Doppelpunkte getrennt werden die Ein- und Ausgaben sowie die &amp;quot;Clobber-Liste&amp;quot; angegeben&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Beispiel für Inline-Assembler ist das Einfügen eine NOP-Anweisung. NOP steht für &#039;&#039;&#039;NO&#039;&#039;&#039;-O&#039;&#039;&#039;P&#039;&#039;&#039;eration. Dieser Assembler-Befehl benötigt genau einen Taktzyklus, ansonsten &amp;quot;tut sich nichts&amp;quot;. Sinnvolle Anwendungen für NOP sind genaue Delay(=warte)-Funktionen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
/* Verzoegern der weiteren Programmausfuehrung um&lt;br /&gt;
   genau 3 Taktzyklen */&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
asm volatile (&amp;quot;nop&amp;quot;::);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann mit einem NOP verhindert werden, dass leere Schleifen, die als Warteschleifen gedacht sind, wegoptimiert werden. Der Compiler erkennt ansonsten die vermeindlich nutzlose Schleife und erzeugt dafür keinen Code im ausführbaren Programm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
uint16_t i;&lt;br /&gt;
&lt;br /&gt;
/* leere Schleife - wird bei eingeschalteter Compiler-Optimierung wegoptimiert */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Schleife erzwingen (keine Optimierung): &amp;quot;NOP-Methode&amp;quot; */&lt;br /&gt;
for (i=0;i&amp;lt;1000;i++) asm volatile(&amp;quot;NOP&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* alternative Methode (keine Optimierung): */&lt;br /&gt;
volatile uint16_t j;&lt;br /&gt;
for (j=0;j&amp;lt;1000;j++);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein weiterer nützliche &amp;quot;Assembler-Einzeiler&amp;quot; ist der Auruf von sleep (&#039;&#039;asm volatile (&amp;quot;sleep&amp;quot;::);&#039;&#039;), da hierzu keine eigene Funktion in der avr-libc existiert.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel für mehrzeiligen Inline-Assembler eine präzise delay-Funktion. Die Funktion erhält ein 16-bit Wort als Parameter, prüft den Parameter auf 0 und beendet die Funktion in diesem Fall oder durchläuft die folgende Schleife sooft wie im Wert des Parameters angegeben. Inline-Assembler hat hier den Vorteil des die Laufzeit unabhängig von der Optimierungsstufe (Parameter -O, vgl. makefile) und der Compiler-Version ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
static inline void delayloop16(uint16_t count)&lt;br /&gt;
{&lt;br /&gt;
	asm volatile (  &amp;quot;cp  %A0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;cpc %B0,__zero_reg__ \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;breq L_Exit_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_LOOP_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;sbiw %0,1            \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;brne L_LOOP_%=       \n\t&amp;quot;  \&lt;br /&gt;
                     &amp;quot;L_Exit_%=:           \n\t&amp;quot;  \&lt;br /&gt;
                     : &amp;quot;=w&amp;quot; (count)&lt;br /&gt;
		     : &amp;quot;0&amp;quot;  (count)&lt;br /&gt;
                   );                            &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Jede Zeile wird mit &#039;&#039;&#039;\n\t&#039;&#039;&#039; abgeschlossen, alle Zeilen mit &#039;&#039;&#039;\&#039;&#039;&#039; verbunden.&lt;br /&gt;
* Sprung-Marken (labels) werden mit einm Prozentzeichen abgeschlossen, der Präprozessor (Assembler?) setzt an dieser Stelle eine laufende Nummer ein, die Doppelbezeichnungen bei mehrmaliger Verwendung somit verhindert.&lt;br /&gt;
&lt;br /&gt;
Detaillierte Ausführungen zum Thema Inline-Assembler finden sich in der Dokumentation der avr-libc.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Dokumentation der avr-libc/Related Pages/Inline Asm&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf AVR Assembler-Anweisungsliste]&lt;br /&gt;
&lt;br /&gt;
== Assembler-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Assembler-Dateien erhalten die Endung .S (&#039;&#039;grosses&#039;&#039; S) und werden im makefile nach WinAVR/mfile-Vorlage hinter &#039;&#039;ASRC=&#039;&#039; durch Leerzeichen getrennt aufgelistet.&lt;br /&gt;
&lt;br /&gt;
...TODO: Beispiele, Parameteruebergabe, globale Variablen für Datenaustausch&lt;br /&gt;
&lt;br /&gt;
= TODO =&lt;br /&gt;
&lt;br /&gt;
stdio.h, malloc() ???, Code-Optimierungen (&amp;quot;tricks&amp;quot;), &amp;quot;naked&amp;quot;-Functionen ??, IO-Register als Parameter/&amp;quot;Variablen&amp;quot; (volatile uint8_t *mybusport; mybusport=&amp;amp;PORTB; void sendbus(uint8_t *parm)...)&lt;br /&gt;
&lt;br /&gt;
= C-Code und Bibliotheken für externe Baugrupen =&lt;br /&gt;
(TODO: sollte in avr-gcc verschoben werden)&lt;br /&gt;
[[Linksammlung#Avr]]&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== Text (character-mode) HD44870 ===&lt;br /&gt;
* [http://jump.to/fleury P.Fleury]&lt;br /&gt;
* avrfreaks Projekt 59 (Chris E.) und andere&lt;br /&gt;
* Procyon avrlib v. Pascal Slang (GPL)&lt;br /&gt;
* Bray&lt;br /&gt;
&lt;br /&gt;
=== Grafik-LCD ===&lt;br /&gt;
==== Nokia3310 ====&lt;br /&gt;
==== Nokia 6100 LCD ====&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;br /&gt;
==== KS0108 ====&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP&lt;br /&gt;
* apetech.de&lt;br /&gt;
==Schnittstellen==&lt;br /&gt;
===I2C===&lt;br /&gt;
====Master====&lt;br /&gt;
[http://jump.to/fleury/ P. Fleurys I2C Master library]&lt;br /&gt;
====Slave====&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* [http://www.atmel.com] Codesammlung zum AT90CAN128&lt;br /&gt;
&lt;br /&gt;
=== DS18S20/1-Wire ===&lt;br /&gt;
* avrfreaks Projekt 59&lt;br /&gt;
* mikrocontroller.net/codesammlung (P. Dannegger)&lt;br /&gt;
&lt;br /&gt;
=== UART ===&lt;br /&gt;
* Procyon avrlib (GPL)&lt;br /&gt;
* avrfreaks UP (viele viele)&lt;br /&gt;
* P. Fleury&lt;br /&gt;
&lt;br /&gt;
== Speicherkarten/IDE/FAT ==&lt;br /&gt;
* avrfreaks UP (IDE/FAT read write)&lt;br /&gt;
== CRC ==&lt;br /&gt;
* avrfreaks-forum (O&#039;Flynn)&lt;br /&gt;
* avr-hal (GPL)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Adressierung&amp;diff=11184</id>
		<title>Adressierung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Adressierung&amp;diff=11184"/>
		<updated>2004-11-07T16:15:19Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Indirekte Adressierung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mikrocontroller und -prozessoren bieten in der Regel mehrere Möglichkeiten an, um auf Daten zuzugreifen. An dieser Stelle sollen die grundlegenden Adressierungsarten der AVR-Controller mit internem SRAM behandelt werden. &amp;lt;!-- wer will, darf das ganze gerne erweitern! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Immediate-Werte==&lt;br /&gt;
Eigentlich keine Adressierungsart, aber dennoch sehr wichtig, ist die Möglichkeit direkt feste Werte in ein Register zu schreiben. Dabei ist schon zur Entwicklungszeit bekannt, welcher Wert in welches Register geladen werden soll.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ldi r16, 0xa0  ;Schreibt den Wert 0xa0 in das Register r16 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ldi&#039;&#039;&#039; steht hierbei für &#039;&#039;&#039;l&#039;&#039;&#039;oa&#039;&#039;&#039;d&#039;&#039;&#039; &#039;&#039;&#039;i&#039;&#039;&#039;mmediate.&lt;br /&gt;
Bei AVR-Mikrocontrollern ist das direkte Laden von Werten nur mit den Registern r16 bis r31 möglich.&lt;br /&gt;
&lt;br /&gt;
==Direkte Adressierung==&lt;br /&gt;
Um auf Daten im Speicher zuzugreifen, muss man selbstverständlich wissen, wo sich diese Daten befinden. Will man z.B. den Inhalt eines Registers in eine Speicherzelle schreiben, so muss das Mikroprogramm die Adresse der gewünschten Speicherzelle kennen.&lt;br /&gt;
Eine einfache Möglichkeit der Adressierung ist es, dem Befehl die Adresse direkt mitzuteilen.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
variable: .byte 1           ;Ein Byte im SRAM reservieren.&lt;br /&gt;
                            ;Da davor das label &amp;quot;variable&amp;quot; steht, wird jedes Vorkommen von&lt;br /&gt;
                            ;&amp;quot;variable&amp;quot; durch die eigentliche Adresse der reservierten&lt;br /&gt;
                            ;Speicherzelle ersetzt&lt;br /&gt;
.cseg&lt;br /&gt;
          ldi r16, 25       ;Den direkten Wert 25 in das Register r16 schreiben (immediate)&lt;br /&gt;
          sts variable, r16 ;Den Inhalt von Register r16 (also 25) in die Speicherzelle&lt;br /&gt;
                            ;mit der Adresse &amp;quot;variable&amp;quot; schreiben. Wie oben beschrieben&lt;br /&gt;
                            ;ersetzt der Assembler &amp;quot;variable&amp;quot; mit der eigentlichen Adresse&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Adresse der Speicherzelle wird also schon zur Entwicklungszeit im Assembler-Befehl eingetragen, was nach sich zieht, dass so ein Befehl nur auf Speicherzellen zugreifen kann, deren Adressen schon im Vorhinein bekannt sind. Da &#039;&#039;variable&#039;&#039; in obigem Beispiel eine Adresse und somit nur eine Zahl darstellt, kann man zur Entwicklungszeit auch Konstanten addieren:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
variable2: .byte 2           ;Zwei Bytes im SRAM reservieren. Dabei ist variable2 die Adresse&lt;br /&gt;
                             ;der ERSTEN Speicherzelle von den beiden reservierten.&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
           ldi r16, 17          ;Den direkten Wert 17 in das Register r16 schreiben (immediate)&lt;br /&gt;
           sts variable2, r16   ;Diesen Wert schreiben wir nun an die Speicheradresse variable2 (1stes Byte)&lt;br /&gt;
           inc r16              ;Register r16 inkrementieren, also um 1 erhöhen.&lt;br /&gt;
           sts variable2+1, r16 ;Hier schreiben wir das zweite Byte von variable2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nun steht in diesem Beispiel im ersten Byte die Zahl 17 und im zweiten Byte die Zahl 18. Dabei muss man beachten, dass die Addition im sts-Befehl bereits während der Assemblierung und nicht vom Mikrocontroller durchgeführt wird. Das ist der Fall, weil die Adresse der reservierten Speicherzelle schon zu dieser Zeit berechnet worden ist. Somit ist natürlich auch die Adresse + 1 bekannt.&lt;br /&gt;
&lt;br /&gt;
==Indirekte Adressierung==&lt;br /&gt;
Wenn wir nur die direkte Adressierung zur Verfügung haben, stossen wir schnell an Grenzen. Betrachten wir folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
Wir sollen Code schreiben, welcher eine variable Anzahl an Zahlen addieren soll. Die Zahlen stehen bereits hintereinander im Speicher, beginnend mit der Adresse &#039;&#039;zahlen_start&#039;&#039;, und im Register r16 steht, wie viele Zahlen es sind. Man merkt leicht, dass dies mit direkter Adressierung nur schwer möglich ist, denn es ist zur Entwicklungszeit noch nicht bekannt, wie viele Zahlen es sind.&lt;br /&gt;
&lt;br /&gt;
Wir lösen diese Aufgabe, indem wir eine Schleife programmieren, die die Zahlen liest und aufaddiert, und das ganze so oft, wie im Register r16 steht. Da wir hier von einer Schleife reden, müssen wir bei jedem Lesen aus dem Speicher mit demselben Befehl auf eine andere Speicherzelle zugreifen. Wir brauchen also die Möglichkeit die Adresse dynamisch im Programmablauf zu ändern. Dieses bietet uns die &#039;&#039;indirekte Adressierung&#039;&#039;, bei der die Adresse der gewünschten Speicherstelle in einem Register steht.&lt;br /&gt;
&lt;br /&gt;
Bei AVR-Mikrocontrollern gibt es dafür drei 16 Bit breite Register, die jeweils aus zwei 8-Bit-Registern bestehen. Dies rührt daher, dass ein 8-Bit-Register nur maximal 256 verschiedene Speicherzellen adressieren könnte, was für Mikrocontroller mit mehr Speicher nicht ausreicht. Die Register (r26, r27) und (r28, r29) und (r30, r31) bilden die besagten drei 16 Bit breiten Register zur indirekten Adressierung. Sie tragen die Namen X, Y und Z, wobei die einzelnen 8-Bit-Register neben ihren r&amp;lt;small&amp;gt;&#039;&#039;xx&#039;&#039;&amp;lt;/small&amp;gt;-Namen auch mit XL, XH, YL, YH, ZL, ZH angesprochen werden können. L(low) und H(high) bedeutet hierbei dass die unteren resp. die oberen 8 Bits der 16-Bit-Adresse gemeint ist.&lt;br /&gt;
&lt;br /&gt;
 Register des AVR:&lt;br /&gt;
 +----------------+&lt;br /&gt;
 |                |&lt;br /&gt;
 | r1 bis r25 ... |&lt;br /&gt;
 |                |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
 | r26 = XL |     |&lt;br /&gt;
 +----------+  X  |&lt;br /&gt;
 | r27 = XH |     |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
 | r28 = YL |     |&lt;br /&gt;
 +----------+  Y  |&lt;br /&gt;
 | r29 = YH |     |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
 | r30 = ZL |     |&lt;br /&gt;
 +----------+  Z  |&lt;br /&gt;
 | r31 = ZH |     |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
&lt;br /&gt;
Wir werden beispielhalber das Z-Register für unser Problem verwenden. Dazu müssen wir zunächst die Adresse der ersten Zahl in dieses laden. Da das Z-Register 16 Bit breit ist, müssen wir ZH und ZL in zwei einzelnen ldi-Operationen beschreiben. Der AVR-Assembler bietet uns hier zwei praktische Funktionen: Mit LOW(...) und HIGH(...) bekommt man die unteren resp. die oberen 8 Bit einer Speicheradresse. Das kommt uns gerade recht, da wir gerade die unteren/oberen 8 Bit der Adresse in die Register ZL/ZH schreiben wollen.&lt;br /&gt;
&lt;br /&gt;
Dann können wir mit dem &#039;&#039;ld&#039;&#039;-Befehl die Zahl von der Speicherstelle lesen, auf die das Z-Register verweist. Wir schreiben den Wert in das Register r17. Zum Aufsummieren wollen wir das Register R18 verwenden, welches wir ganz zu Anfang mit &#039;&#039;clr&#039;&#039; auf 0 setzen.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
zahlen_start: .byte 20   ; Wir reservieren 20 Byte, das soll unsere Maximalanzahl sein&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
              ...   ;Irgendwo vorher werden die Zahlen geschrieben, das interessiert uns&lt;br /&gt;
                    ;erstmal nicht weiter, wie das geschieht. Wir gehen jetzt davon aus,&lt;br /&gt;
                    ;dass beginnend bei der Speicheradresse zahlen_start so viele Zahlen&lt;br /&gt;
                    ;im Speicher stehen, wie im Register r16 steht.&lt;br /&gt;
&lt;br /&gt;
              ldi ZL, LOW(zahlen_start)  ;ZL mit den unteren Bits der Adresse initialisieren&lt;br /&gt;
              ldi ZH, HIGH(zahlen_start) ;ZH mit den oberen Bits der Adresse initialisieren&lt;br /&gt;
              clr r18                    ;r18 auf 0 initialisieren&lt;br /&gt;
schleife:&lt;br /&gt;
              ld r17, Z      ;Inhalt der von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
              adiw ZH:ZL, 1  ;Z inkrementieren, da wir gleich die darauffolgende&lt;br /&gt;
                             ;Zahl lesen wollen. adiw eignet sich für 16-Bit-Addition&lt;br /&gt;
              add r18, r17   ;Aufsummieren&lt;br /&gt;
&lt;br /&gt;
              dec r16        ;Wir erniedrigen r16 um 1&lt;br /&gt;
              brne schleife  ;Solange r16 ungleich 0, zu &amp;quot;schleife&amp;quot; springen&lt;br /&gt;
              &lt;br /&gt;
              ;An dieser Stelle ist die Schleife fertig und in r18 steht unser Ergebnis.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das Programm funktioniert zwar schon, aber eine Sache ist unpraktisch: wir müssen jedes mal Z manuell inkrementieren, um im nächsten Schleifendurchlauf die nächste Zahl zu lesen. Da das sequenzielle Lesen oder Schreiben von Daten aus dem bzw. in das SRAM sehr oft in Programmen vorkommt, gibt es folgende Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
===Postinkrement===&lt;br /&gt;
Die beiden Zeilen&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ld r17, Z      ;Inhalt der von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
adiw ZH:ZL, 1  ;Z inkrementieren&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
können wir durch folgende Zeile ersetzen&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ld r17, Z+      ;Inhalt der von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
                ;und danach Z automatisch inkrementieren&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das spart Ausführungszeit und macht den Code kürzer. Zu beachten ist, dass die Inkrementierung erst &#039;&#039;&#039;nach&#039;&#039;&#039; der Ausführung des eigentlichen Befehls durchgeführt wird.&lt;br /&gt;
===Predekrement===&lt;br /&gt;
Äquivalent zum Postinkrement gibt es auch die Möglichkeit des Dekrementierens. Hierbei wird der Wert jedoch &#039;&#039;&#039;vor&#039;&#039;&#039; der Ausführung des Befehls dekrementiert. Das Predekrement eignet sich zum &amp;quot;rückwärts&amp;quot; durchgehen von linear angeordneten Daten.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ld r17, -Z      ;Z dekrementieren und DANACH Inhalt der &lt;br /&gt;
                ;von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=68HC12&amp;diff=4711</id>
		<title>68HC12</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=68HC12&amp;diff=4711"/>
		<updated>2004-11-04T20:43:35Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* 68HC12 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* 16-Bit-Datenbus(nach aussen 8 Bit)&lt;br /&gt;
* Internes [[RAM]] und Flash-[[EEPROM]]&lt;br /&gt;
* 2 8-Bit Akkumulatoren&lt;br /&gt;
* [[I2C]],[[SPI]],[[AD-Wandler]],mehrere Ports, [[Timer]]&lt;br /&gt;
* Multiplikation und Division&lt;br /&gt;
&lt;br /&gt;
== Externe Links ==&lt;br /&gt;
&lt;br /&gt;
[http://www.lemps.ch LEMPS12] &lt;br /&gt;
System zur Ausbildung in Microcontroller-Programmierung mit dem 68HC812A4&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=68HC12&amp;diff=4708</id>
		<title>68HC12</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=68HC12&amp;diff=4708"/>
		<updated>2004-11-04T20:43:10Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* 68HC12 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== 68HC12 ==&lt;br /&gt;
&lt;br /&gt;
* 16-Bit-Datenbus(nach aussen 8 Bit)&lt;br /&gt;
* Internes [[RAM]] und Flash-[[EEPROM]]&lt;br /&gt;
* 2 8-Bit Akkumulatoren&lt;br /&gt;
* [[I2C]],[[SPI]],[[AD-Wandler]],mehrere Ports, [[Timer]]&lt;br /&gt;
* Multiplikation und Division&lt;br /&gt;
&lt;br /&gt;
== Externe Links ==&lt;br /&gt;
&lt;br /&gt;
[http://www.lemps.ch LEMPS12] &lt;br /&gt;
System zur Ausbildung in Microcontroller-Programmierung mit dem 68HC812A4&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:AVR_Assembler_-_Vergleichstabelle&amp;diff=4714</id>
		<title>Diskussion:AVR Assembler - Vergleichstabelle</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:AVR_Assembler_-_Vergleichstabelle&amp;diff=4714"/>
		<updated>2004-11-03T19:58:10Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ich habe gerade eben geändert, dass der ATmega8 doch JMP und CALL unterstützt, jetzt allerdings festgestellt, dass es da widersprüchliche Angaben gibt. Laut Hilfedatei im AVR Studio gehören diese Befehle zum Instruction set, laut Datenblatt des ATmega8 jedoch nicht. Was ist nun richtig? --[[Benutzer:Oxygene|Oxygene]] 13:16, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
Im Zweifel hat immer das Datenblatt recht. JMP und CALL würden beim Mega8 auch keinen Sinn machen da +-2k (von rjmp und rcall) völlig reichen um den gesamten Addressraum zu erreichen --[[Benutzer:Matthias|Matthias]] 17:06, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
:Ja, habe das auch ausprobiert und es geht nicht. Die Hilfedatei kann man in der Hinsicht &#039;&#039;&#039;garnicht&#039;&#039;&#039; gebrauchen, da sind nämlich z.B. auch EIJMP u.ä. drin, obwohl das kein µC unterstützt. --[[Benutzer:Oxygene|Oxygene]] 19:13, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
Hi, wäre es möglich die Tabelle, also die Spalten, irgendwie logischer zu gruppieren :) Beispielsweise die Megas und Tinys je nacheinander?&lt;br /&gt;
:Kann man natürlich machen, ich mach da gleich mal fix was. --[[Benutzer:Oxygene|Oxygene]] 19:14, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Habe die Tabelle jetzt mal (hoffentlich) schön und leserlich gemacht. Allerdings ist es jetzt natürlich nicht trivial, neue Spalten hinzuzufügen. Die Erstellung des Tabellen-Markups mache ich daher in einem kleinen PHP-Script. Wer also eine weitere Spalte hinzufügen will, der gebe mir etwas in folgendem Format. Die Reihenfolge der Zeichen entspricht den einzelnen Instructions in der Tabelle von oben nach unten gelesen (jeweils Gruppenweise).&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
$mega8 = array(  &amp;quot;++++++++++++++++++++++++++++&amp;quot;,&lt;br /&gt;
                 &amp;quot;++--++--++++++++++++++++++++++++++++++&amp;quot;,&lt;br /&gt;
                 &amp;quot;++++++++++-+++++&amp;quot;,&lt;br /&gt;
                 &amp;quot;++++++++++++++++++++++++++++&amp;quot;,&lt;br /&gt;
                 &amp;quot;-+++&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
--[[Benutzer:Oxygene|Oxygene]] 20:42, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
Super :) und gleich deutlich besser lesbar. Vielen Dank. --[[Benutzer:Yahp|Yahp]] 20:56, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
Wen es interessiert, hier das quick-n-dirty-PHP-script: http://oxygene.localhost.li/php/wiki.phps --[[Benutzer:Oxygene|Oxygene]] 20:58, 3. Nov 2004 (CET)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:AVR_Assembler_-_Vergleichstabelle&amp;diff=4700</id>
		<title>Diskussion:AVR Assembler - Vergleichstabelle</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:AVR_Assembler_-_Vergleichstabelle&amp;diff=4700"/>
		<updated>2004-11-03T19:42:13Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ich habe gerade eben geändert, dass der ATmega8 doch JMP und CALL unterstützt, jetzt allerdings festgestellt, dass es da widersprüchliche Angaben gibt. Laut Hilfedatei im AVR Studio gehören diese Befehle zum Instruction set, laut Datenblatt des ATmega8 jedoch nicht. Was ist nun richtig? --[[Benutzer:Oxygene|Oxygene]] 13:16, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
Im Zweifel hat immer das Datenblatt recht. JMP und CALL würden beim Mega8 auch keinen Sinn machen da +-2k (von rjmp und rcall) völlig reichen um den gesamten Addressraum zu erreichen --[[Benutzer:Matthias|Matthias]] 17:06, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
:Ja, habe das auch ausprobiert und es geht nicht. Die Hilfedatei kann man in der Hinsicht &#039;&#039;&#039;garnicht&#039;&#039;&#039; gebrauchen, da sind nämlich z.B. auch EIJMP u.ä. drin, obwohl das kein µC unterstützt. --[[Benutzer:Oxygene|Oxygene]] 19:13, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
Hi, wäre es möglich die Tabelle, also die Spalten, irgendwie logischer zu gruppieren :) Beispielsweise die Megas und Tinys je nacheinander?&lt;br /&gt;
:Kann man natürlich machen, ich mach da gleich mal fix was. --[[Benutzer:Oxygene|Oxygene]] 19:14, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Habe die Tabelle jetzt mal (hoffentlich) schön und leserlich gemacht. Allerdings ist es jetzt natürlich nicht trivial, neue Spalten hinzuzufügen. Die Erstellung des Tabellen-Markups mache ich daher in einem kleinen PHP-Script. Wer also eine weitere Spalte hinzufügen will, der gebe mir etwas in folgendem Format. Die Reihenfolge der Zeichen entspricht den einzelnen Instructions in der Tabelle von oben nach unten gelesen (jeweils Gruppenweise).&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
$mega8 = array(  &amp;quot;++++++++++++++++++++++++++++&amp;quot;,&lt;br /&gt;
                 &amp;quot;++--++--++++++++++++++++++++++++++++++&amp;quot;,&lt;br /&gt;
                 &amp;quot;++++++++++-+++++&amp;quot;,&lt;br /&gt;
                 &amp;quot;++++++++++++++++++++++++++++&amp;quot;,&lt;br /&gt;
                 &amp;quot;-+++&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
--[[Benutzer:Oxygene|Oxygene]] 20:42, 3. Nov 2004 (CET)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Assembler_-_Vergleichstabelle&amp;diff=7212</id>
		<title>AVR Assembler - Vergleichstabelle</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Assembler_-_Vergleichstabelle&amp;diff=7212"/>
		<updated>2004-11-03T19:36:24Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Entstanden aus dem Forenbeitrag: [http://www.mikrocontroller.net/forum/read-1-124179.html Tabelle: welcher AVR welche Befehle?]&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellpadding=&amp;quot;3&amp;quot; cellspacing=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |Arithmetic and Logic Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ADD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ADC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ADIW&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SUB&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SUBI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SBC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBCI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SBIW&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | AND&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ANDI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | OR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ORI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | EOR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | COM&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | NEG&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SBR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | CBR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | INC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | DEC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | TST&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | CLR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SER&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | MUL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | MULS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | MULSU&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | FMUL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | FMULS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | FMULSU&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |Branch Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | RJMP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | IJMP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | EIJMP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | JMP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | RCALL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ICALL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | EICALL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CALL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | RET&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | RETI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | CPSE&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | CPC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CPI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBRC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SBRS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBIC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SBIS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRBS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRBC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BREQ&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRNE&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRCS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRCC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRSH&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRLO&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRMI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRPL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRGE&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRLT&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRHS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRHC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRTS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRTC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRVS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRVC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRIE&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRID&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |Data Transfer Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | MOV&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | MOVW&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | LDI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | LDS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | LD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | LDD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | STS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ST&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | STD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | LPM&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ELPM&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SPM&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | IN&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | OUT&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | PUSH&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | POP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |Bit and Bit-test Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CBI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | LSL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | LSR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ROL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ROR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ASR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SWAP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BSET&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BCLR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BST&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BLD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEN&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLN&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEZ&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLZ&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SES&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEV&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLV&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SET&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLT&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEH&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLH&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |MCU Control Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BREAK&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | NOP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SLEEP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | WDR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Assembler_-_Vergleichstabelle&amp;diff=4698</id>
		<title>AVR Assembler - Vergleichstabelle</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Assembler_-_Vergleichstabelle&amp;diff=4698"/>
		<updated>2004-11-03T19:34:03Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: FMULSU&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Entstanden aus dem Forenbeitrag: [http://www.mikrocontroller.net/forum/read-1-124179.html Tabelle: welcher AVR welche Befehle?]&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellpadding=&amp;quot;3&amp;quot; cellspacing=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |Arithmetic and Logic Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ADD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ADC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ADIW&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SUB&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SUBI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SBC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBCI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | AND&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ANDI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | OR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ORI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | EOR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | COM&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | NEG&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CBR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | INC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | DEC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | TST&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SER&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | MUL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | MULS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | MULSU&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | FMUL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | FMULS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | FMULSU&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |Branch Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | RJMP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | IJMP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | EIJMP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | JMP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | RCALL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ICALL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | EICALL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CALL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | RET&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | RETI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | CPSE&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | CPC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CPI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBRC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SBRS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBIC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SBIS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRBS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRBC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BREQ&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRNE&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRCS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRCC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRSH&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRLO&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRMI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRPL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRGE&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRLT&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRHS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRHC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRTS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRTC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRVS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRVC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRIE&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRID&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |Data Transfer Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | MOV&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | MOVW&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | LDI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | LDS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | LD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | LDD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | STS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ST&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | STD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | LPM&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ELPM&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SPM&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | IN&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | OUT&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | PUSH&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | POP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |Bit and Bit-test Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CBI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | LSL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | LSR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ROL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ROR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ASR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SWAP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BSET&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BCLR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BST&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BLD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEN&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLN&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEZ&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLZ&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SES&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEV&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLV&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SET&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLT&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEH&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLH&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |MCU Control Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BREAK&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | NOP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SLEEP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | WDR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Assembler_-_Vergleichstabelle&amp;diff=4697</id>
		<title>AVR Assembler - Vergleichstabelle</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Assembler_-_Vergleichstabelle&amp;diff=4697"/>
		<updated>2004-11-03T19:31:31Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Entstanden aus dem Forenbeitrag: [http://www.mikrocontroller.net/forum/read-1-124179.html Tabelle: welcher AVR welche Befehle?]&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot; cellpadding=&amp;quot;3&amp;quot; cellspacing=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |Arithmetic and Logic Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ADD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ADC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ADIW&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SUB&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SUBI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SBC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBCI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | AND&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ANDI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | OR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ORI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | EOR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | COM&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | NEG&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CBR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | INC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | DEC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | TST&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SER&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | MUL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | MULS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | MULSU&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | FMUL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | FMULS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | FMULS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |Branch Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | RJMP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | IJMP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | EIJMP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | JMP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | RCALL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ICALL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | EICALL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CALL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | RET&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | RETI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | CPSE&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | CPC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CPI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBRC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SBRS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBIC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SBIS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRBS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRBC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BREQ&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRNE&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRCS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRCC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRSH&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRLO&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRMI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRPL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRGE&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRLT&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRHS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRHC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRTS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRTC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRVS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRVC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BRIE&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BRID&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |Data Transfer Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | MOV&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | MOVW&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | LDI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | LDS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | LD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | LDD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | STS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ST&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | STD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | LPM&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ELPM&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SPM&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f0f0f0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | IN&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | OUT&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | PUSH&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | POP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #f7f7f7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |Bit and Bit-test Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SBI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CBI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | LSL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | LSR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ROL&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | ROR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | ASR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | SWAP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BSET&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BCLR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BST&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | BLD&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLC&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEN&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLN&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEZ&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLZ&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLI&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SES&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLS&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEV&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLV&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SET&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLT&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SEH&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | CLH&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:black; color:#ffdead;&amp;quot; colspan=&amp;quot;7&amp;quot; |MCU Control Instructions&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#fff0bf;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |90S2313&amp;lt;br&amp;gt;90S8515&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny11/12&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny13&amp;lt;br&amp;gt;tiny2313&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |tiny26&lt;br /&gt;
| style=&amp;quot;background:#ffdead; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega8&lt;br /&gt;
| style=&amp;quot;background:#fff0bf; vertical-align: bottom; font-weight: bold; text-align:left;&amp;quot; |mega16&amp;lt;br&amp;gt;mega162&amp;lt;br&amp;gt;mega32&amp;lt;br&amp;gt;mega64&amp;lt;br&amp;gt;mega128&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | BREAK&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e7e7e7;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: red; background: #e0e0e0;&amp;quot; | ×&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | NOP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #ffdead;&amp;quot; | SLEEP&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e0e0e0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #e7e7e7;&amp;quot; | ?&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background: #fff0bf;&amp;quot; | WDR&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f0f0f0;&amp;quot; | ?&lt;br /&gt;
| style=&amp;quot;font-weight: bold; text-align: center; color: green; background: #f7f7f7;&amp;quot; | ?&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:AVR_Assembler_-_Vergleichstabelle&amp;diff=4699</id>
		<title>Diskussion:AVR Assembler - Vergleichstabelle</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:AVR_Assembler_-_Vergleichstabelle&amp;diff=4699"/>
		<updated>2004-11-03T18:14:00Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ich habe gerade eben geändert, dass der ATmega8 doch JMP und CALL unterstützt, jetzt allerdings festgestellt, dass es da widersprüchliche Angaben gibt. Laut Hilfedatei im AVR Studio gehören diese Befehle zum Instruction set, laut Datenblatt des ATmega8 jedoch nicht. Was ist nun richtig? --[[Benutzer:Oxygene|Oxygene]] 13:16, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
Im Zweifel hat immer das Datenblatt recht. JMP und CALL würden beim Mega8 auch keinen Sinn machen da +-2k (von rjmp und rcall) völlig reichen um den gesamten Addressraum zu erreichen --[[Benutzer:Matthias|Matthias]] 17:06, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
:Ja, habe das auch ausprobiert und es geht nicht. Die Hilfedatei kann man in der Hinsicht &#039;&#039;&#039;garnicht&#039;&#039;&#039; gebrauchen, da sind nämlich z.B. auch EIJMP u.ä. drin, obwohl das kein µC unterstützt. --[[Benutzer:Oxygene|Oxygene]] 19:13, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
Hi, wäre es möglich die Tabelle, also die Spalten, irgendwie logischer zu gruppieren :) Beispielsweise die Megas und Tinys je nacheinander?&lt;br /&gt;
:Kann man natürlich machen, ich mach da gleich mal fix was. --[[Benutzer:Oxygene|Oxygene]] 19:14, 3. Nov 2004 (CET)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:AVR_Assembler_-_Vergleichstabelle&amp;diff=4695</id>
		<title>Diskussion:AVR Assembler - Vergleichstabelle</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:AVR_Assembler_-_Vergleichstabelle&amp;diff=4695"/>
		<updated>2004-11-03T18:13:42Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ich habe gerade eben geändert, dass der ATmega8 doch JMP und CALL unterstützt, jetzt allerdings festgestellt, dass es da widersprüchliche Angaben gibt. Laut Hilfedatei im AVR Studio gehören diese Befehle zum Instruction set, laut Datenblatt des ATmega8 jedoch nicht. Was ist nun richtig? --[[Benutzer:Oxygene|Oxygene]] 13:16, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
Im Zweifel hat immer das Datenblatt recht. JMP und CALL würden beim Mega8 auch keinen Sinn machen da +-2k (von rjmp und rcall) völlig reichen um den gesamten Addressraum zu erreichen --[[Benutzer:Matthias|Matthias]] 17:06, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
:Ja, habe das auch ausprobiert und es geht nicht. Die Hilfedatei kann man in der Hinsicht &#039;&#039;&#039;garnicht&#039;&#039;&#039; gebrauchen, da sind nämlich z.B. auch EIJMP u.ä. drin, obwohl das kein µC unterstützt. --[[Benutzer:Oxygene|Oxygene]] 19:13, 3. Nov 2004 (CET)&lt;br /&gt;
&lt;br /&gt;
Hi, wäre es möglich die Tabelle, also die Spalten, irgendwie logischer zu gruppieren :) Beispielsweise die Megas und Tinys je nacheinander?&lt;br /&gt;
:Kann man natürlich machen, ich mach da gleich mal fix was.&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Assembler_-_Vergleichstabelle&amp;diff=4696</id>
		<title>AVR Assembler - Vergleichstabelle</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Assembler_-_Vergleichstabelle&amp;diff=4696"/>
		<updated>2004-11-03T12:28:56Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Entstanden aus dem Forenbeitrag: [http://www.mikrocontroller.net/forum/read-1-124179.html Tabelle: welcher AVR welche Befehle?]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;table&amp;quot;&amp;gt;&lt;br /&gt;
  Instr.\Device  m8    S8515   m128   m64    m162    m32     t11/12     t13   t2313   m16     t26   S2313&lt;br /&gt;
 =========================================================================================================&lt;br /&gt;
&lt;br /&gt;
  Arithmetic and Logic Instructions&lt;br /&gt;
  ---------------------------------&lt;br /&gt;
  ADD            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ADC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ADIW           +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  SUB            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SUBI           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBCI           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBIW           +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  AND            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ANDI           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  OR             +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ORI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  EOR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  COM            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  NEG            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CBR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  INC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  DEC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  TST            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SER            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  MUL            +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  MULS           +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  MULSU          +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  FMUL           +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  FMULS          +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  FMULSU         +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  &lt;br /&gt;
  Branch Instructions&lt;br /&gt;
  -------------------&lt;br /&gt;
  RJMP           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  IJMP           +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  EIJMP          -     -       -      -      -       -       -          -     -       -       -     -&lt;br /&gt;
  JMP            -     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  RCALL          +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ICALL          +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  EICALL         -     -       -      -      -       -       -          -     -       -       -     -&lt;br /&gt;
  CALL           -     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  RET            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  RETI           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CPSE           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CP             +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CPC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CPI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBRC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBRS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBIC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBIS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRBS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRBC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BREQ           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRNE           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRCS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRCC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRSH           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRLO           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRMI           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRPL           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRGE           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRLT           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRHS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRHC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRTS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRTC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRVS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRVC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRIE           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRID           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  &lt;br /&gt;
  Data Transfer Instructions&lt;br /&gt;
  --------------------------&lt;br /&gt;
  MOV            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  MOVW           +     -       +      +      +       +       -          +     +       +       -     -&lt;br /&gt;
  LDI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  LDS            +     +       +      +      +       +       -          +     +       +       -     +&lt;br /&gt;
  LD             +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  LDD            +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  STS            +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  ST             +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  STD            +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  LPM            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ELPM           -     -       +      -      -       -       -          -     -       -       -     -&lt;br /&gt;
  SPM            +     -       +      +      +       +       -          +     +       +       -     -&lt;br /&gt;
  IN             +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  OUT            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  PUSH           +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  POP            +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  &lt;br /&gt;
  Bit and Bit-test Instructions&lt;br /&gt;
  -----------------------------&lt;br /&gt;
  SBI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CBI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  LSL            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  LSR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ROL            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ROR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ASR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SWAP           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BSET           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BCLR           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BST            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BLD            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SEC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SEN            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLN            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SEZ            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLZ            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SEI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SES            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLS            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SEV            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLV            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SET            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLT            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SEH            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLH            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
&lt;br /&gt;
  MCU Control Instructions&lt;br /&gt;
  ------------------------&lt;br /&gt;
  BREAK          -     -       +      +      +       +       -          +     +       +       -     -&lt;br /&gt;
  NOP            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SLEEP          +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  WDR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:AVR_Assembler_-_Vergleichstabelle&amp;diff=4692</id>
		<title>Diskussion:AVR Assembler - Vergleichstabelle</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:AVR_Assembler_-_Vergleichstabelle&amp;diff=4692"/>
		<updated>2004-11-03T12:16:41Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ich habe gerade eben geändert, dass der ATmega8 doch JMP und CALL unterstützt, jetzt allerdings festgestellt, dass es da widersprüchliche Angaben gibt. Laut Hilfedatei im AVR Studio gehören diese Befehle zum Instruction set, laut Datenblatt des ATmega8 jedoch nicht. Was ist nun richtig? --[[Benutzer:Oxygene|Oxygene]] 13:16, 3. Nov 2004 (CET)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Assembler_-_Vergleichstabelle&amp;diff=4691</id>
		<title>AVR Assembler - Vergleichstabelle</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Assembler_-_Vergleichstabelle&amp;diff=4691"/>
		<updated>2004-11-03T12:05:43Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: der ATmega8 hat sehr wohl JMP und CALL&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Entstanden aus dem Forenbeitrag: [http://www.mikrocontroller.net/forum/read-1-124179.html Tabelle: welcher AVR welche Befehle?]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;table&amp;quot;&amp;gt;&lt;br /&gt;
  Instr.\Device  m8    S8515   m128   m64    m162    m32     t11/12     t13   t2313   m16     t26   S2313&lt;br /&gt;
 =========================================================================================================&lt;br /&gt;
&lt;br /&gt;
  Arithmetic and Logic Instructions&lt;br /&gt;
  ---------------------------------&lt;br /&gt;
  ADD            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ADC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ADIW           +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  SUB            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SUBI           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBCI           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBIW           +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  AND            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ANDI           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  OR             +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ORI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  EOR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  COM            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  NEG            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CBR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  INC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  DEC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  TST            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SER            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  MUL            +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  MULS           +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  MULSU          +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  FMUL           +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  FMULS          +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  FMULSU         +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  &lt;br /&gt;
  Branch Instructions&lt;br /&gt;
  -------------------&lt;br /&gt;
  RJMP           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  IJMP           +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  EIJMP          -     -       -      -      -       -       -          -     -       -       -     -&lt;br /&gt;
  JMP            +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  RCALL          +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ICALL          +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  EICALL         -     -       -      -      -       -       -          -     -       -       -     -&lt;br /&gt;
  CALL           +     -       +      +      +       +       -          -     -       +       -     -&lt;br /&gt;
  RET            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  RETI           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CPSE           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CP             +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CPC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CPI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBRC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBRS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBIC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SBIS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRBS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRBC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BREQ           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRNE           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRCS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRCC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRSH           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRLO           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRMI           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRPL           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRGE           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRLT           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRHS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRHC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRTS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRTC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRVS           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRVC           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRIE           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BRID           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  &lt;br /&gt;
  Data Transfer Instructions&lt;br /&gt;
  --------------------------&lt;br /&gt;
  MOV            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  MOVW           +     -       +      +      +       +       -          +     +       +       -     -&lt;br /&gt;
  LDI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  LDS            +     +       +      +      +       +       -          +     +       +       -     +&lt;br /&gt;
  LD             +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  LDD            +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  STS            +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  ST             +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  STD            +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  LPM            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ELPM           -     -       +      -      -       -       -          -     -       -       -     -&lt;br /&gt;
  SPM            +     -       +      +      +       +       -          +     +       +       -     -&lt;br /&gt;
  IN             +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  OUT            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  PUSH           +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  POP            +     +       +      +      +       +       -          +     +       +       +     +&lt;br /&gt;
  &lt;br /&gt;
  Bit and Bit-test Instructions&lt;br /&gt;
  -----------------------------&lt;br /&gt;
  SBI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CBI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  LSL            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  LSR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ROL            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ROR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  ASR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SWAP           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BSET           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BCLR           +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BST            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  BLD            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SEC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLC            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SEN            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLN            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SEZ            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLZ            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SEI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLI            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SES            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLS            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SEV            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLV            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SET            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLT            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SEH            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  CLH            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
&lt;br /&gt;
  MCU Control Instructions&lt;br /&gt;
  ------------------------&lt;br /&gt;
  BREAK          -     -       +      +      +       +       -          +     +       +       -     -&lt;br /&gt;
  NOP            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  SLEEP          +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
  WDR            +     +       +      +      +       +       +          +     +       +       +     +&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Adressierung&amp;diff=4724</id>
		<title>Adressierung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Adressierung&amp;diff=4724"/>
		<updated>2004-11-02T15:04:05Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Indirekte Adressierung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mikrocontroller und -prozessoren bieten in der Regel mehrere Möglichkeiten an, um auf Daten zuzugreifen. An dieser Stelle sollen die grundlegenden Adressierungsarten der AVR-Controller mit internem SRAM behandelt werden. &amp;lt;!-- wer will, darf das ganze gerne erweitern! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Immediate-Werte==&lt;br /&gt;
Eigentlich keine Adressierungsart, aber dennoch sehr wichtig, ist die Möglichkeit direkt feste Werte in ein Register zu schreiben. Dabei ist schon zur Entwicklungszeit bekannt, welcher Wert in welches Register geladen werden soll.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ldi r16, 0xa0  ;Schreibt den Wert 0xa0 in das Register r16 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ldi&#039;&#039;&#039; steht hierbei für &#039;&#039;&#039;l&#039;&#039;&#039;oa&#039;&#039;&#039;d&#039;&#039;&#039; &#039;&#039;&#039;i&#039;&#039;&#039;mmediate.&lt;br /&gt;
Bei AVR-Mikrocontrollern ist das direkte Laden von Werten nur mit den Registern r16 bis r31 möglich.&lt;br /&gt;
&lt;br /&gt;
==Direkte Adressierung==&lt;br /&gt;
Um auf Daten im Speicher zuzugreifen, muss man selbstverständlich wissen, wo sich diese Daten befinden. Will man z.B. den Inhalt eines Registers in eine Speicherzelle schreiben, so muss das Mikroprogramm die Adresse der gewünschten Speicherzelle kennen.&lt;br /&gt;
Eine einfache Möglichkeit der Adressierung ist es, dem Befehl die Adresse direkt mitzuteilen.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
variable: .byte 1           ;Ein Byte im SRAM reservieren.&lt;br /&gt;
                            ;Da davor das label &amp;quot;variable&amp;quot; steht, wird jedes Vorkommen von&lt;br /&gt;
                            ;&amp;quot;variable&amp;quot; durch die eigentliche Adresse der reservierten&lt;br /&gt;
                            ;Speicherzelle ersetzt&lt;br /&gt;
.cseg&lt;br /&gt;
          ldi r16, 25       ;Den direkten Wert 25 in das Register r16 schreiben (immediate)&lt;br /&gt;
          sts variable, r16 ;Den Inhalt von Register r16 (also 25) in die Speicherzelle&lt;br /&gt;
                            ;mit der Adresse &amp;quot;variable&amp;quot; schreiben. Wie oben beschrieben&lt;br /&gt;
                            ;ersetzt der Assembler &amp;quot;variable&amp;quot; mit der eigentlichen Adresse&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Adresse der Speicherzelle wird also schon zur Entwicklungszeit im Assembler-Befehl eingetragen, was nach sich zieht, dass so ein Befehl nur auf Speicherzellen zugreifen kann, deren Adressen schon im Vorhinein bekannt sind. Da &#039;&#039;variable&#039;&#039; in obigem Beispiel eine Adresse und somit nur eine Zahl darstellt, kann man zur Entwicklungszeit auch Konstanten addieren:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
variable2: .byte 2           ;Zwei Bytes im SRAM reservieren. Dabei ist variable2 die Adresse&lt;br /&gt;
                             ;der ERSTEN Speicherzelle von den beiden reservierten.&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
           ldi r16, 17          ;Den direkten Wert 17 in das Register r16 schreiben (immediate)&lt;br /&gt;
           sts variable2, r16   ;Diesen Wert schreiben wir nun an die Speicheradresse variable2 (1stes Byte)&lt;br /&gt;
           inc r16              ;Register r16 inkrementieren, also um 1 erhöhen.&lt;br /&gt;
           sts variable2+1, r16 ;Hier schreiben wir das zweite Byte von variable2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nun steht in diesem Beispiel im ersten Byte die Zahl 17 und im zweiten Byte die Zahl 18. Dabei muss man beachten, dass die Addition im sts-Befehl bereits während der Assemblierung und nicht vom Mikrocontroller durchgeführt wird. Das ist der Fall, weil die Adresse der reservierten Speicherzelle schon zu dieser Zeit berechnet worden ist. Somit ist natürlich auch die Adresse + 1 bekannt.&lt;br /&gt;
&lt;br /&gt;
==Indirekte Adressierung==&lt;br /&gt;
Wenn wir nur die direkte Adressierung zur Verfügung haben, stossen wir schnell an Grenzen. Betrachten wir folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
Wir sollen Code schreiben, welcher eine variable Anzahl an Zahlen addieren soll. Die Zahlen stehen bereits hintereinander im Speicher, beginnend mit der Adresse &#039;&#039;zahlen_start&#039;&#039;, und im Register r16 steht, wie viele Zahlen es sind. Man merkt leicht, dass dies mit direkter Adressierung nur schwer möglich ist, denn es ist zur Entwicklungszeit noch nicht bekannt, wie viele Zahlen es sind.&lt;br /&gt;
&lt;br /&gt;
Wir lösen diese Aufgabe, indem wir eine Schleife programmieren, die die Zahlen liest und aufaddiert, und das ganze so oft, wie im Register r16 steht. Da wir hier von einer Schleife reden, müssen wir bei jedem Lesen aus dem Speicher mit demselben Befehl auf eine andere Speicherzelle zugreifen. Wir brauchen also die Möglichkeit die Adresse dynamisch im Programmablauf zu ändern. Dieses bietet uns die &#039;&#039;indirekte Adressierung&#039;&#039;, bei der die Adresse der gewünschten Speicherstelle in einem Register steht.&lt;br /&gt;
&lt;br /&gt;
Bei AVR-Mikrocontrollern gibt es dafür drei 16 Bit breite Register, die jeweils aus zwei 8-Bit-Registern bestehen. Dies rührt daher, dass ein 8-Bit-Register nur maximal 256 verschiedene Speicherzellen adressieren könnte, was für Mikrocontroller mit mehr Speicher nicht ausreicht. Die Register (r26, r27) und (r28, r29) und (r30, r31) bilden jeweils die besagten drei 16 Bit breiten Register zur indirekten Adressierung. Sie tragen die Namen X, Y und Z, wobei die einzelnen 8-Bit-Register neben ihren r&#039;&#039;xx&#039;&#039;-Namen auch mit XL, XH, YL, YH, ZL, ZH angesprochen werden können. L(low) und H(high) bedeutet hierbei dass die unteren resp. die oberen 8 Bits der 16-Bit-Adresse gemeint ist.&lt;br /&gt;
&lt;br /&gt;
 Register des AVR:&lt;br /&gt;
 +----------------+&lt;br /&gt;
 |                |&lt;br /&gt;
 | r1 bis r25 ... |&lt;br /&gt;
 |                |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
 | r26 = XL |     |&lt;br /&gt;
 +----------+  X  |&lt;br /&gt;
 | r27 = XH |     |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
 | r28 = YL |     |&lt;br /&gt;
 +----------+  Y  |&lt;br /&gt;
 | r29 = YH |     |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
 | r30 = ZL |     |&lt;br /&gt;
 +----------+  Z  |&lt;br /&gt;
 | r31 = ZH |     |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
&lt;br /&gt;
Wir werden beispielhalber das Z-Register für unser Problem verwenden. Dazu müssen wir zunächst die Adresse der ersten Zahl in dieses laden. Da das Z-Register 16 Bit breit ist, müssen wir ZH und ZL in zwei einzelnen ldi-Operationen beschreiben. Der AVR-Assembler bietet uns hier zwei praktische Funktionen: Mit LOW(...) und HIGH(...) bekommt man die unteren resp. die oberen 8 Bit einer Speicheradresse. Das kommt uns gerade recht, da wir gerade die unteren/oberen 8 Bit der Adresse in die Register ZL/ZH schreiben wollen.&lt;br /&gt;
&lt;br /&gt;
Dann können wir mit dem &#039;&#039;ld&#039;&#039;-Befehl die Zahl von der Speicherstelle lesen, auf die das Z-Register verweist. Wir schreiben den Wert in das Register r17. Zum Aufsummieren wollen wir das Register R18 verwenden, welches wir ganz zu Anfang mit &#039;&#039;clr&#039;&#039; auf 0 setzen.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
zahlen_start: .byte 20   ; Wir reservieren 20 Byte, das soll unsere Maximalanzahl sein&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
              ...   ;Irgendwo vorher werden die Zahlen geschrieben, das interessiert uns&lt;br /&gt;
                    ;erstmal nicht weiter, wie das geschieht. Wir gehen jetzt davon aus,&lt;br /&gt;
                    ;dass beginnend bei der Speicheradresse zahlen_start so viele Zahlen&lt;br /&gt;
                    ;im Speicher stehen, wie im Register r16 steht.&lt;br /&gt;
&lt;br /&gt;
              ldi ZL, LOW(zahlen_start)  ;ZH mit den unteren Bits der Adresse initialisieren&lt;br /&gt;
              ldi ZH, HIGH(zahlen_start) ;ZH mit den oberen Bits der Adresse initialisieren&lt;br /&gt;
              clr r18                    ;r18 auf 0 initialisieren&lt;br /&gt;
schleife:&lt;br /&gt;
              ld r17, Z      ;Inhalt der von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
              adiw ZH:ZL, 1  ;Z inkrementieren, da wir gleich die darauffolgende&lt;br /&gt;
                             ;Zahl lesen wollen. adiw eignet sich für 16-Bit-Addition&lt;br /&gt;
              add R18, r17   ;Aufsummieren&lt;br /&gt;
&lt;br /&gt;
              dec r16        ;Wir erniedrigen r16 um 1&lt;br /&gt;
              brne schleife  ;Solange r16 ungleich 0, zu &amp;quot;schleife&amp;quot; springen&lt;br /&gt;
              &lt;br /&gt;
              ;An dieser Stelle ist die Schleife fertig und in r18 steht unser Ergebnis.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das Programm funktioniert zwar schon, aber eine Sache ist unpraktisch: wir müssen jedes mal Z manuell inkrementieren, um im nächsten Schleifendurchlauf die nächste Zahl zu lesen. Da das sequenzielle Lesen oder Schreiben von Daten aus dem bzw. in das SRAM sehr oft in Programmen vorkommt, gibt es folgende Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
===Postinkrement===&lt;br /&gt;
Die beiden Zeilen&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ld r17, Z      ;Inhalt der von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
adiw ZH:ZL, 1  ;Z inkrementieren, da wir gleich die darauffolgende&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
können wir durch folgende Zeile ersetzen&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ld r17, Z+      ;Inhalt der von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
                ;und danach Z automatisch inkrementieren&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das Spart Ausführungszeit und macht den Code kürzer. Zu beachten ist, dass die Inkrementierung erst &#039;&#039;&#039;nach&#039;&#039;&#039; der Ausführung des eigentlichen Befehls durchgeführt wird.&lt;br /&gt;
===Predekrement===&lt;br /&gt;
Äquivalent zum Postinkrement gibt es auch die Möglichkeit des Dekrementierens. Hierbei wird der Wert jedoch &#039;&#039;&#039;vor&#039;&#039;&#039; der Ausführung des Befehls dekrementiert. Das Predekrement eignet sich zum &amp;quot;rückwärts&amp;quot; durchgehen von linear angeordneten Daten.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ld r17, -Z      ;Z dekrementieren und DANACH Inhalt der &lt;br /&gt;
                ;von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Adressierung&amp;diff=4665</id>
		<title>Adressierung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Adressierung&amp;diff=4665"/>
		<updated>2004-11-02T14:57:40Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mikrocontroller und -prozessoren bieten in der Regel mehrere Möglichkeiten an, um auf Daten zuzugreifen. An dieser Stelle sollen die grundlegenden Adressierungsarten der AVR-Controller mit internem SRAM behandelt werden. &amp;lt;!-- wer will, darf das ganze gerne erweitern! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Immediate-Werte==&lt;br /&gt;
Eigentlich keine Adressierungsart, aber dennoch sehr wichtig, ist die Möglichkeit direkt feste Werte in ein Register zu schreiben. Dabei ist schon zur Entwicklungszeit bekannt, welcher Wert in welches Register geladen werden soll.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ldi r16, 0xa0  ;Schreibt den Wert 0xa0 in das Register r16 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ldi&#039;&#039;&#039; steht hierbei für &#039;&#039;&#039;l&#039;&#039;&#039;oa&#039;&#039;&#039;d&#039;&#039;&#039; &#039;&#039;&#039;i&#039;&#039;&#039;mmediate.&lt;br /&gt;
Bei AVR-Mikrocontrollern ist das direkte Laden von Werten nur mit den Registern r16 bis r31 möglich.&lt;br /&gt;
&lt;br /&gt;
==Direkte Adressierung==&lt;br /&gt;
Um auf Daten im Speicher zuzugreifen, muss man selbstverständlich wissen, wo sich diese Daten befinden. Will man z.B. den Inhalt eines Registers in eine Speicherzelle schreiben, so muss das Mikroprogramm die Adresse der gewünschten Speicherzelle kennen.&lt;br /&gt;
Eine einfache Möglichkeit der Adressierung ist es, dem Befehl die Adresse direkt mitzuteilen.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
variable: .byte 1           ;Ein Byte im SRAM reservieren.&lt;br /&gt;
                            ;Da davor das label &amp;quot;variable&amp;quot; steht, wird jedes Vorkommen von&lt;br /&gt;
                            ;&amp;quot;variable&amp;quot; durch die eigentliche Adresse der reservierten&lt;br /&gt;
                            ;Speicherzelle ersetzt&lt;br /&gt;
.cseg&lt;br /&gt;
          ldi r16, 25       ;Den direkten Wert 25 in das Register r16 schreiben (immediate)&lt;br /&gt;
          sts variable, r16 ;Den Inhalt von Register r16 (also 25) in die Speicherzelle&lt;br /&gt;
                            ;mit der Adresse &amp;quot;variable&amp;quot; schreiben. Wie oben beschrieben&lt;br /&gt;
                            ;ersetzt der Assembler &amp;quot;variable&amp;quot; mit der eigentlichen Adresse&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Adresse der Speicherzelle wird also schon zur Entwicklungszeit im Assembler-Befehl eingetragen, was nach sich zieht, dass so ein Befehl nur auf Speicherzellen zugreifen kann, deren Adressen schon im Vorhinein bekannt sind. Da &#039;&#039;variable&#039;&#039; in obigem Beispiel eine Adresse und somit nur eine Zahl darstellt, kann man zur Entwicklungszeit auch Konstanten addieren:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
variable2: .byte 2           ;Zwei Bytes im SRAM reservieren. Dabei ist variable2 die Adresse&lt;br /&gt;
                             ;der ERSTEN Speicherzelle von den beiden reservierten.&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
           ldi r16, 17          ;Den direkten Wert 17 in das Register r16 schreiben (immediate)&lt;br /&gt;
           sts variable2, r16   ;Diesen Wert schreiben wir nun an die Speicheradresse variable2 (1stes Byte)&lt;br /&gt;
           inc r16              ;Register r16 inkrementieren, also um 1 erhöhen.&lt;br /&gt;
           sts variable2+1, r16 ;Hier schreiben wir das zweite Byte von variable2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nun steht in diesem Beispiel im ersten Byte die Zahl 17 und im zweiten Byte die Zahl 18. Dabei muss man beachten, dass die Addition im sts-Befehl bereits während der Assemblierung und nicht vom Mikrocontroller durchgeführt wird. Das ist der Fall, weil die Adresse der reservierten Speicherzelle schon zu dieser Zeit berechnet worden ist. Somit ist natürlich auch die Adresse + 1 bekannt.&lt;br /&gt;
&lt;br /&gt;
==Indirekte Adressierung==&lt;br /&gt;
Wenn wir nur die direkte Adressierung zur Verfügung haben, stossen wir schnell an Grenzen. Betrachten wir folgendes Beispiel:&lt;br /&gt;
&lt;br /&gt;
Wir sollen Code schreiben, welcher eine Variable Anzahl an Zahlen addieren soll. Die Zahlen stehen bereits hintereinander im Speicher, beginnend mit der Adresse &#039;&#039;zahlen_start&#039;&#039; und im Register r16 steht, wie viele Zahlen es sind. Man merkt leicht, dass dies mit direkter Adressierung nur schwer möglich ist, denn es ist zur Entwicklungszeit noch nicht bekannt, wie viele Zahlen es denn sind.&lt;br /&gt;
&lt;br /&gt;
Wir lösen diese Aufgabe, indem wir eine Schleife programmieren, die die Zahlen liest und aufaddiert, und das ganze so oft, wie im Register r16 steht. Da wir hier von einer Schleife reden, müssen wir bei jedem Lesen aus dem Speicher auf eine andere Speicherzelle zugreifen. Wir brauchen also die Möglichkeit die Adresse dynamisch im Programmablauf zu ändern. Dieses bietet uns die &#039;&#039;indirekte Adressierung&#039;&#039;, bei der die Adresse der gewünschten Speicherstelle in einem Register steht.&lt;br /&gt;
&lt;br /&gt;
Bei AVR-Mikrocontrollern gibt es dafür 3 &amp;quot;virtuelle&amp;quot; Register, die jeweils aus zwei 8-Bit-Registern bestehen. Dies rührt daher, dass ein 8-Bit-Register nur maximal 256 verschiedene Speicherzellen adressieren könnte, was für Mikrocontroller mit mehr Speicher zu wenig ist. Die Register r26, r27 und r28, r29 und r30, r31 bilden jeweils paarweise die besagten drei 16-Bit breiten Register zur indirekten Adressierung. Sie tragen die Namen X, Y und Z, wobei die einzelnen 8-Bit-Register neben ihren r&#039;&#039;xx&#039;&#039;-Namen auch mit XL, XH, YL, YH, ZL, ZH angesprochen werden können. L und H entspricht hierbei den unteren resp. den oberen 8 Bits der 16-Bit-Adresse.&lt;br /&gt;
&lt;br /&gt;
 Register des AVR:&lt;br /&gt;
 +----------------+&lt;br /&gt;
 |                |&lt;br /&gt;
 | r1 bis r25     |&lt;br /&gt;
 |                |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
 | r26 = XL |     |&lt;br /&gt;
 +----------+  X  |&lt;br /&gt;
 | r27 = XH |     |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
 | r28 = YL |     |&lt;br /&gt;
 +----------+  Y  |&lt;br /&gt;
 | r29 = YH |     |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
 | r30 = ZL |     |&lt;br /&gt;
 +----------+  Z  |&lt;br /&gt;
 | r31 = ZH |     |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
&lt;br /&gt;
Wir werden beispielhalber das Z-Register für unser Problem verwenden. Dazu müssen wir zunächst die Adresse der ersten Zahl in dieses laden. Da das Z-Register ja 16 Bit breit ist, müssen wir einzeln ZH und ZL in zwei ldi-Operationen schreiben. Der AVR-Assembler bietet uns hier zwei praktische Funktionen: Mit LOW(...) und HIGH(...) bekommt man die unteren resp. die oberen 8 Bit einer Speicheradresse. Das kommt uns gerade recht, da wir ja gerade die unteren/oberen 8 Bit der Adresse in die Register ZL/ZH schreiben wollen.&lt;br /&gt;
&lt;br /&gt;
Dann können wir mit dem &#039;&#039;ld&#039;&#039;-Befehl die Zahl von der Speicherstelle lesen, auf die das Z-Register verweist. Wir schreiben den Wert in r17. Zum Aufsummieren wollen wir das Register R18 verwenden, welches wir ganz zu Anfang mit &#039;&#039;clr&#039;&#039; auf 0 setzen.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
zahlen_start: .byte 20   ; Wir reservieren 20 Byte, das soll unsere Maximalanzahl sein&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
              ...   ;Irgendwo vorher werden die Zahlen geschrieben, das interessiert uns&lt;br /&gt;
                    ;erstmal nicht weiter, wie das geschieht. Wir gehen jetzt davon aus,&lt;br /&gt;
                    ;dass beginnend bei der Speicheradresse zahlen_start so viele Zahlen&lt;br /&gt;
                    ;im Speicher stehen, wie im Register r16 steht.&lt;br /&gt;
&lt;br /&gt;
              ldi ZL, LOW(zahlen_start)  ;ZH mit den unteren Bits der Adresse initialisieren&lt;br /&gt;
              ldi ZH, HIGH(zahlen_start) ;ZH mit den oberen Bits der Adresse initialisieren&lt;br /&gt;
              clr r18                    ;r18 auf 0 initialisieren&lt;br /&gt;
schleife:&lt;br /&gt;
              ld r17, Z      ;Inhalt der von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
              adiw ZH:ZL, 1  ;Z inkrementieren, da wir gleich die darauffolgende&lt;br /&gt;
                             ;Zahl lesen wollen. adiw eignet sich für 16-Bit-Addition&lt;br /&gt;
              add R18, r17   ;Aufsummieren&lt;br /&gt;
&lt;br /&gt;
              dec r16        ;Wir erniedrigen r16 um 1&lt;br /&gt;
              brne schleife  ;Solange r16 ungleich 0, zu &amp;quot;schleife&amp;quot; springen&lt;br /&gt;
              &lt;br /&gt;
              ;An dieser Stelle ist die Schleife fertig und in r18 steht unser Ergebnis.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unser Programm funktioniert zwar schon, aber eine Sache ist unpraktisch: wir müssen jedes mal Z manuell inkrementieren. Das das sequenzielle Lesen oder Schreiben von Daten aus dem bzw. in das SRAM sehr oft vorkommt gibt es folgende Möglichkeit:&lt;br /&gt;
&lt;br /&gt;
===Postinkrement===&lt;br /&gt;
Die beiden Zeilen&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ld r17, Z      ;Inhalt der von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
adiw ZH:ZL, 1  ;Z inkrementieren, da wir gleich die darauffolgende&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
können wir durch folgende Zeile ersetzen&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ld r17, Z+      ;Inhalt der von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
                ;und danach Z automatisch inkrementieren&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das Spart Ausführungszeit und macht den Code kürzer. Zu beachten ist, dass die Inkrementierung erst &#039;&#039;&#039;nach&#039;&#039;&#039; der Ausführung des eigentlichen Befehls durchgeführt wird.&lt;br /&gt;
===Predekrement===&lt;br /&gt;
Äquivalent zum Postinkrement gibt es auch die Möglichkeit des Dekrementierens. Hierbei wird der Wert jedoch &#039;&#039;&#039;vor&#039;&#039;&#039; der Ausführung des Befehls dekrementiert:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ld r17, -Z      ;Z dekrementieren und DANACH Inhalt der &lt;br /&gt;
                ;von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Adressierung&amp;diff=4664</id>
		<title>Adressierung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Adressierung&amp;diff=4664"/>
		<updated>2004-11-02T14:52:17Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mikrocontroller und -prozessoren bieten in der Regel mehrere Möglichkeiten an, um auf Daten zuzugreifen. An dieser Stelle sollen die grundlegenden Adressierungsarten der AVR-Controller mit internem SRAM behandelt werden. &amp;lt;!-- wer will, darf das ganze gerne erweitern! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Immediate-Werte==&lt;br /&gt;
Eigentlich keine Adressierungsart, aber dennoch sehr wichtig, ist die Möglichkeit direkt feste Werte in ein Register zu schreiben. Dabei ist schon zur Entwicklungszeit bekannt, welcher Wert in welches Register geladen werden soll.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ldi r16, 0xa0  ;Schreibt den Wert 0xa0 in das Register r16 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;ldi&#039;&#039;&#039; steht hierbei für &#039;&#039;&#039;l&#039;&#039;&#039;oa&#039;&#039;&#039;d&#039;&#039;&#039; &#039;&#039;&#039;i&#039;&#039;&#039;mmediate.&lt;br /&gt;
Bei AVR-Mikrocontrollern ist das direkte Laden von Werten nur mit den Registern r16 bis r31 möglich.&lt;br /&gt;
&lt;br /&gt;
==Direkte Adressierung==&lt;br /&gt;
Um auf Daten im Speicher zuzugreifen, muss man selbstverständlich wissen, wo sich diese Daten befinden. Will man z.B. den Inhalt eines Registers in eine Speicherzelle schreiben, so muss das Mikroprogramm die Adresse der gewünschten Speicherzelle kennen.&lt;br /&gt;
Eine einfache Möglichkeit der Adressierung ist es, dem Befehl die Adresse direkt mitzuteilen.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
variable: .byte 1           ;Ein Byte im SRAM reservieren.&lt;br /&gt;
                            ;Da davor das label &amp;quot;variable&amp;quot; steht, wird jedes Vorkommen von&lt;br /&gt;
                            ;&amp;quot;variable&amp;quot; durch die eigentliche Adresse der reservierten&lt;br /&gt;
                            ;Speicherzelle ersetzt&lt;br /&gt;
.cseg&lt;br /&gt;
          ldi r16, 25       ;Den direkten Wert 25 in das Register r16 schreiben (immediate)&lt;br /&gt;
          sts variable, r16 ;Den Inhalt von Register r16 (also 25) in die Speicherzelle&lt;br /&gt;
                            ;mit der Adresse &amp;quot;variable&amp;quot; schreiben. Wie oben beschrieben&lt;br /&gt;
                            ;ersetzt der Assembler &amp;quot;variable&amp;quot; mit der eigentlichen Adresse&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Adresse der Speicherzelle wird also schon zur Entwicklungszeit im Assembler-Befehl eingetragen, was nach sich zieht, dass so ein Befehl nur auf Speicherzellen zugreifen kann, deren Adressen schon im Vorhinein bekannt sind. Da &#039;&#039;variable&#039;&#039; in obigem Beispiel eine Adresse und somit nur eine Zahl darstellt, kann man zur Entwicklungszeit auch Konstanten addieren:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
variable2: .byte 2           ;Zwei Bytes im SRAM reservieren. Dabei ist variable2 die Adresse&lt;br /&gt;
                             ;der ERSTEN Speicherzelle von den beiden reservierten.&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
           ldi r16, 17          ;Den direkten Wert 17 in das Register r16 schreiben (immediate)&lt;br /&gt;
           sts variable2, r16   ;Diesen Wert schreiben wir nun an die Speicheradresse variable2 (1stes Byte)&lt;br /&gt;
           inc r16              ;Register r16 inkrementieren, also um 1 erhöhen.&lt;br /&gt;
           sts variable2+1, r16 ;Hier schreiben wir das zweite Byte von variable2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nun steht in diesem Beispiel im ersten Byte die Zahl 17 und im zweiten Byte die Zahl 18. Dabei muss man beachten, dass die Addition im sts-Befehl bereits während der Assemblierung und nicht vom Mikrocontroller durchgeführt wird. Das ist der Fall, weil die Adresse der reservierten Speicherzelle schon zu dieser Zeit berechnet worden ist. Somit ist natürlich auch die Adresse + 1 bekannt.&lt;br /&gt;
&lt;br /&gt;
==Indirekte Adressierung==&lt;br /&gt;
Wenn wir nur die direkte Adressierung zur Verfügung haben, stossen wir schnell an Grenzen. Betrachten wir folgendes Beispiel:&lt;br /&gt;
Wir sollen Code schreiben, welcher eine Variable Anzahl an Zahlen addieren soll. Die Zahlen stehen bereits hintereinander im Speicher, beginnend mit der Adresse &#039;&#039;zahlen_start&#039;&#039; und im Register r16 steht, wie viele Zahlen es sind. Man merkt leicht, dass dies mit direkter Adressierung nur schwer möglich ist, denn es ist zur Entwicklungszeit noch nicht bekannt, wie viele Zahlen es denn sind.&lt;br /&gt;
Wir lösen diese Aufgabe, indem wir eine Schleife programmieren, die die Zahlen liest und aufaddiert, und das ganze so oft, wie im Register r16 steht. Da wir hier von einer Schleife reden, müssen wir bei jedem Lesen aus dem Speicher auf eine andere Speicherzelle zugreifen. Wir brauchen also die Möglichkeit die Adresse dynamisch im Programmablauf zu ändern. Dieses bietet uns die &#039;&#039;indirekte Adressierung&#039;&#039;, bei der die Adresse der gewünschten Speicherstelle in einem Register steht.&lt;br /&gt;
Bei AVR-Mikrocontrollern gibt es dafür 3 &amp;quot;virtuelle&amp;quot; Register, die jeweils aus zwei 8-Bit-Registern bestehen. Dies rührt daher, dass ein 8-Bit-Register nur maximal 256 verschiedene Speicherzellen adressieren könnte, was für Mikrocontroller mit mehr Speicher zu wenig ist. Die Register r26, r27 und r28, r29 und r30, r31 bilden jeweils paarweise die besagten drei 16-Bit breiten Register zur indirekten Adressierung. Sie tragen die Namen X, Y und Z, wobei die einzelnen 8-Bit-Register neben ihren r&#039;&#039;xx&#039;&#039;-Namen auch mit XL, XH, YL, YH, ZL, ZH angesprochen werden können. L und H entspricht hierbei den unteren resp. den oberen 8 Bits der 16-Bit-Adresse.&lt;br /&gt;
&lt;br /&gt;
 Register des AVR:&lt;br /&gt;
 +----------------+&lt;br /&gt;
 |                |&lt;br /&gt;
 | r1 bis r25     |&lt;br /&gt;
 |                |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
 | r26 = XL |     |&lt;br /&gt;
 +----------+  X  |&lt;br /&gt;
 | r27 = XH |     |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
 | r28 = YL |     |&lt;br /&gt;
 +----------+  Y  |&lt;br /&gt;
 | r29 = YH |     |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
 | r30 = ZL |     |&lt;br /&gt;
 +----------+  Z  |&lt;br /&gt;
 | r31 = ZH |     |&lt;br /&gt;
 +----------+-----+&lt;br /&gt;
&lt;br /&gt;
Wir werden beispielhalber das Z-Register für unser Problem verwenden. Dazu müssen wir zunächst die Adresse der ersten Zahl in dieses laden. Da das Z-Register ja 16 Bit breit ist, müssen wir einzeln ZH und ZL in zwei ldi-Operationen schreiben. Der AVR-Assembler bietet uns hier zwei praktische Funktionen: Mit LOW(...) und HIGH(...) bekommt man die unteren resp. die oberen 8 Bit einer Speicheradresse. Das kommt uns gerade recht, da wir ja gerade die unteren/oberen 8 Bit der Adresse in die Register ZL/ZH schreiben wollen.&lt;br /&gt;
Dann können wir mit dem &#039;&#039;ld&#039;&#039;-Befehl die Zahl von der Speicherstelle lesen, auf die das Z-Register verweist. Wir schreiben den Wert in r17. Zum Aufsummieren wollen wir das Register R18 verwenden, welches wir ganz zu Anfang mit &#039;&#039;clr&#039;&#039; auf 0 setzen.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
zahlen_start: .byte 20   ; Wir reservieren 20 Byte, das soll unsere Maximalanzahl sein&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
              ...   ;Irgendwo vorher werden die Zahlen geschrieben, das interessiert uns&lt;br /&gt;
                    ;erstmal nicht weiter, wie das geschieht. Wir gehen jetzt davon aus,&lt;br /&gt;
                    ;dass beginnend bei der Speicheradresse zahlen_start so viele Zahlen&lt;br /&gt;
                    ;im Speicher stehen, wie im Register r16 steht.&lt;br /&gt;
&lt;br /&gt;
              ldi ZL, LOW(zahlen_start)  ;ZH mit den unteren Bits der Adresse initialisieren&lt;br /&gt;
              ldi ZH, HIGH(zahlen_start) ;ZH mit den oberen Bits der Adresse initialisieren&lt;br /&gt;
              clr r18                    ;r18 auf 0 initialisieren&lt;br /&gt;
schleife:&lt;br /&gt;
              ld r17, Z      ;Inhalt der von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
              adiw ZH:ZL, 1  ;Z inkrementieren, da wir gleich die darauffolgende&lt;br /&gt;
                             ;Zahl lesen wollen. adiw eignet sich für 16-Bit-Addition&lt;br /&gt;
              add R18, r17   ;Aufsummieren&lt;br /&gt;
&lt;br /&gt;
              dec r16        ;Wir erniedrigen r16 um 1&lt;br /&gt;
              brne schleife  ;Solange r16 ungleich 0, zu &amp;quot;schleife&amp;quot; springen&lt;br /&gt;
              &lt;br /&gt;
              ;An dieser Stelle ist die Schleife fertig und in r18 steht unser Ergebnis.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unser Programm funktioniert zwar schon, aber eine Sache ist unpraktisch: wir müssen jedes mal Z manuell inkrementieren. Das das sequenzielle Lesen oder Schreiben von Daten aus dem bzw. in das SRAM sehr oft vorkommt gibt es folgende Möglichkeit:&lt;br /&gt;
&lt;br /&gt;
===Postinkrement===&lt;br /&gt;
Die beiden Zeilen&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ld r17, Z      ;Inhalt der von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
adiw ZH:ZL, 1  ;Z inkrementieren, da wir gleich die darauffolgende&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
können wir durch folgende Zeile ersetzen&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ld r17, Z+      ;Inhalt der von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
                ;und danach Z automatisch inkrementieren&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das Spart Ausführungszeit und macht den Code kürzer. Zu beachten ist, dass die Inkrementierung erst &#039;&#039;&#039;nach&#039;&#039;&#039; der Ausführung des eigentlichen Befehls durchgeführt wird.&lt;br /&gt;
===Predekrement===&lt;br /&gt;
Äquivalent zum Postinkrement gibt es auch die Möglichkeit des Dekrementierens. Hierbei wird der Wert jedoch &#039;&#039;&#039;vor&#039;&#039;&#039; der Ausführung des Befehls dekrementiert:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
ld r17, -Z      ;Z dekrementieren und DANACH Inhalt der &lt;br /&gt;
                ;von Z adressierten Speicherstelle in r17 lesen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Butterfly&amp;diff=4398</id>
		<title>AVR Butterfly</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Butterfly&amp;diff=4398"/>
		<updated>2004-10-05T22:36:20Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
Der [http://www.atmel.com/products/AVR/butterfly AVR-Butterfly] ist ein Demo-Board für den ATMega169V [[AVR]]-Controller von [[Atmel]]. Der Preis liegt zwischen ca. 19 und 35 Euro, dies ist für die gebotene Ausstattung sehr günstig.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Avr-butterfly.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Aufbau/Bestandteile ==&lt;br /&gt;
&lt;br /&gt;
* AVR ATMega169V Controller (low-voltage-Ausführung, 16kB Flash, LCD-Controller)&lt;br /&gt;
* [[LCD]]-Display (6 Stellen)&lt;br /&gt;
* &amp;quot;Joystick&amp;quot;&lt;br /&gt;
* [[RS-232]]-Schnittstelle&lt;br /&gt;
* 4MBit Flash-Speicher ([[DataFlash]])&lt;br /&gt;
* Piezo-Lautsprecher&lt;br /&gt;
* Lichtsensor (LDR)&lt;br /&gt;
* Temperatursensor (NTC)&lt;br /&gt;
* Anschluss für Spannungsmessung (0-5V)&lt;br /&gt;
* USI Schnittstelle (für [[I2C]], [[SPI]] und Software-[[UART]])&lt;br /&gt;
* [[JTAG]]-Schnittstelle&lt;br /&gt;
* Knopfzelle&lt;br /&gt;
&lt;br /&gt;
Man kann aber auch eigene Programme erstellen und auf den Butterfly mittels RS232-Verbindung übertragen. Es ist dazu keine besondere Programmierhardware erforderlich, ein einfaches Kabel zum seriellen Anschluss genügt (siehe: TODO nach unten verlinken).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Programme für den AVR-Butterfly erstellen ==&lt;br /&gt;
&lt;br /&gt;
Der Butterfly wird mit einer vorinstallierten Anwendung geliefert, die bis auf das DataFlash und die USI-Schnittstelle die Funktion aller Komponenten demonstriert. Der Quellcode dieser Anwendung (in [[C]]) ist frei verfügbar und kann als Grundlage für eigene Entwicklungen genutzt werden. &lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller auf dem Butterfly ist ein ATmega169V, man kann also Programme für den Controller wie für jeden anderen AVR in Assembler, C, Basic oder Pascal erstellt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Anwendungsprogramm zum AVR-Butterfly übertragen ==&lt;br /&gt;
&lt;br /&gt;
Die vom Compiler bzw. Assembler erzeugte HEX-Datei mit dem Anwendungsprogramm kann über den vorinstallierten Bootloader, das ISP-Interface, die JTAG-Schnittstelle oder im &amp;quot;Parallel-Programming-Mode&amp;quot; auf den Mikrocontroller (ATmega169V) übertragen werden.&lt;br /&gt;
&lt;br /&gt;
=== Seriell über Bootloader ===&lt;br /&gt;
Diese Methode ist die einfachste und auch die sicherste Art der Programmierung. Mithilfe des vorinstallierten Bootloaders kann der Code schnell übertragen werden, benötigt keine besondere Hardware, lediglich ein &amp;quot;Adapterkabel&amp;quot; (TODO Programmierkabel). Die Vorgehensweise und die Anschlussbelegung ist im &amp;quot;Butterfly User-Guide&amp;quot; beschrieben. Software zur Übertragung ist [[AVR-Studio]] und aktuelle Versionen von [[AVRDUDE]] (letzteres auch für UNIX/BSD-Systeme). In diesem Modus ist es nicht möglich, Fuse oder Lockbits zu verändern, man kann also nichts &amp;quot;wirklich kaputt machen&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== (low-voltage-serial) ISP Programmierung ===&lt;br /&gt;
&lt;br /&gt;
Über den ISP-Abschluss (Jxyz)am Butterfly werden Anwendungen im &amp;quot;low-voltage-serial-programming-mode&amp;quot; zum ATmega übertragen. Dazu kann jeder ISP-Programmer genutzt werden, der mit der empfohlenen maximalen Betriebsspannung des Butterfly (4,5V) zurecht kommt (STK500, ATMEL AVRISP, AVR910, Evertool, eingeschänkt mglw. auch STK200-Dongles). (TODO wiki-verlinkung). Programmiersoftware ist jede zur Programmierhardware passende (AVRStudio, yaap, avrdude, uisp etc.). &lt;br /&gt;
&lt;br /&gt;
Mit dieser Methode ist es möglich, Grundeinstellungen (Fuse- und Lockbits) zu ändern, was mit der Programmierung über Bootloader nicht möglich ist. Unerfahrene Anwender sollten hierbei jedoch besondere Vorsicht walten lassen. Auch der vorinstallierte Bootloader kann über ISP gelöscht werden (&amp;quot;Chip-Erase&amp;quot;). Man kann also damit einiges so verstellen, dass der Controller nur mit hohen Aufwand wieder in den Auslieferungszustand versetzt werden kann. Also: vor Benutzung Datenblatt lesen!&lt;br /&gt;
&lt;br /&gt;
=== JTAG Programmierung/Debugging ===&lt;br /&gt;
&lt;br /&gt;
Über die ATmega JTAG-Schnittstelle, die am Butterfly an Jzxy herausgeführt ist, kann der Controller über das Atmel JTAGICE (oder &amp;quot;Nachbau&amp;quot;) programmiert werden. Zusätzlich besteht die Möglichkeit, das Programm &amp;quot;im Controller&amp;quot; zu debuggen. Näheres dazu im Butterfly User-Manual und in der AVRStuido-Online-Hilfe. Programmiersoftware ist AVRStudio und (TODO: OS-Programm fuer JTAGICE). &lt;br /&gt;
&lt;br /&gt;
=== Parallel-Programming-Mode ===&lt;br /&gt;
&lt;br /&gt;
Dieser Modus kann nur nach umlöten einiger (sehr kleiner) Bauteile auf dem Butterfly genutzt werden. Er dient sozusagen als &amp;quot;letzte Rettung&amp;quot; falls sonst kein Zugriff mehr möglich ist, z.B. bei stark &amp;quot;verstellten&amp;quot; Fusebits (z.B. Taktquelle auf externen Oszillator/Quarz). Hardware ist das STK500, das dazu entsprechend &amp;quot;gejumpert&amp;quot; werden muss und die Software AVRStudio. Die Vorgehensweise ist detailliert im Users-Manual und in der AVRStudio Online-Hilfe beschrieben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* http://www.atmel.com/products/AVR/butterfly Butterfly &amp;quot;Homepage&amp;quot; bei Atmel (Dokumentation, Quellcode der vorinstallierten Sortware für IAR-Compiler)&lt;br /&gt;
* http://www.dwelch.com/avr/ Butterfly Einführung (auch Assembler-Beispiele)&lt;br /&gt;
* http://www.siwawi.arubi.uni-kl.de/avr_projects (gcc-Portierung der vorinstallierten Software, Linksammlung zu Butterfly-Informationen, Beispielanwendungen etc.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- * http://cgi.segor.de/user-cgi-bin/sidestep2.pl?foto=1&amp;amp;Q=butterfly&amp;amp;M=1# &lt;br /&gt;
mthomas-&amp;gt;&amp;quot;segor link einfueger&amp;quot;: was lernt man aus der verlinkten seite, ausser dass von segor butterfly-boards verkauft werden? nichts gegen segor als anbieter ansonsten schwer erhaeltlicher teile, aber der link ist hier deplatziert. man koennte eine anbieterliste erstellen. mir fallen ca. 5 fuer d-land ein. dabei waere der segor-preis allerdings bei den hoechsten --&amp;gt; &lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[STK500]]&lt;br /&gt;
* [[AVR-GCC-Tutorial]]&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Butterfly&amp;diff=4391</id>
		<title>AVR Butterfly</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Butterfly&amp;diff=4391"/>
		<updated>2004-10-05T19:34:39Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: Formulierung/Satzbau geändert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
Der [http://www.atmel.com/products/AVR/butterfly AVR-Butterfly] ist ein Demo-Board für den ATMega169V [[AVR]]-Controller von [[Atmel]]. &lt;br /&gt;
&lt;br /&gt;
[[Bild:Avr-butterfly.jpg]]&lt;br /&gt;
&lt;br /&gt;
Bestandteile:&lt;br /&gt;
&lt;br /&gt;
* AVR ATMega169V Controller (low-voltage-Ausführung, 16kB Flash, LCD-Controller)&lt;br /&gt;
* [[LCD]]-Display (6 Stellen)&lt;br /&gt;
* &amp;quot;Joystick&amp;quot;&lt;br /&gt;
* [[RS-232]]-Schnittstelle&lt;br /&gt;
* 4MBit Flash-Speicher ([[DataFlash]])&lt;br /&gt;
* Piezo-Lautsprecher&lt;br /&gt;
* Lichtsensor (LDR)&lt;br /&gt;
* Temperatursensor (NTC)&lt;br /&gt;
* Anschluss für Spannungsmessung (0-5V)&lt;br /&gt;
* USI Schnittstelle (für [[I2C]], [[SPI]] und Software-[[UART]])&lt;br /&gt;
* [[JTAG]]-Schnittstelle&lt;br /&gt;
* Knopfzelle&lt;br /&gt;
&lt;br /&gt;
Der Butterfly wird mit einer vorinstallierten Anwendung geliefert, die bis auf das DataFlash und die USI-Schnittstelle die Funktion aller Komponenten demonstriert. &lt;br /&gt;
&lt;br /&gt;
Man kann aber auch eigene Programme erstellen und auf den Butterfly mittels RS232-Verbindung übertragen. Es ist dazu keine besondere Programmierhardware erforderlich, ein einfaches Kabel zum seriellen Anschluss genügt. Software zur Übertragung ist [[AVR-Studio]] und aktuelle Versionen von [[AVRDUDE]] (letzteres auch für UNIX/BSD-Systeme).&lt;br /&gt;
&lt;br /&gt;
Der Preis liegt zwischen ca. 19 und 35 Euro, dies ist für die gebotene Ausstattung sehr günstig.&lt;br /&gt;
&lt;br /&gt;
== AVR-Butterfly programmieren ==&lt;br /&gt;
=== Seriell===&lt;br /&gt;
Die serielle Methode ist die einfachste und auch die sicherste Art der Programmierung. Mithilfe des vorinstallierten Bootloaders kann der Code schnell übertragen werden. (TODO Programmierkabel)&lt;br /&gt;
=== Parallel ===&lt;br /&gt;
Bei der parallelen Methode ist es möglich, Grundeinstellungen (Fuse- und Lockbits) zu ändern, was mit der seriellen Methode nicht möglich ist. Unerfahrene Anwender sollten hierbei jedoch besondere Vorsicht walten lassen. Bei der parallelen Programmierung ist zudem darauf zu achten, dass die Programmierspannung eingehalten wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- vielleicht kann jemand noch etwas genaueres dazu schreiben? --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* http://www.atmel.com/products/AVR/butterfly Butterfly &amp;quot;Homepage&amp;quot; bei Atmel (Dokumentation, Quellcode der vorinstallierten Sortware für IAR-Compiler)&lt;br /&gt;
* http://www.dwelch.com/avr/ Butterfly Einführung (auch Assembler-Beispiele)&lt;br /&gt;
* http://www.siwawi.arubi.uni-kl.de/avr_projects (gcc-Portierung der vorinstallierten Software, Linksammlung zu Butterfly-Informationen, Beispielanwendungen etc.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- * http://cgi.segor.de/user-cgi-bin/sidestep2.pl?foto=1&amp;amp;Q=butterfly&amp;amp;M=1# &lt;br /&gt;
mthomas-&amp;gt;&amp;quot;segor link einfueger&amp;quot;: was lernt man aus der verlinkten seite, ausser dass von segor butterfly-boards verkauft werden? nichts gegen segor als anbieter ansonsten schwer erhaeltlicher teile, aber der link ist hier deplatziert. man koennte eine anbieterliste erstellen. mir fallen ca. 5 fuer d-land ein. dabei waere der segor-preis allerdings bei den hoechsten --&amp;gt; &lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[STK500]]&lt;br /&gt;
*[[AVR-GCC-Tutorial]]&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:Linksammlung&amp;diff=4684</id>
		<title>Diskussion:Linksammlung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:Linksammlung&amp;diff=4684"/>
		<updated>2004-10-03T08:27:51Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Diese Seite soll moeglicherweise die Linksammlung auf http://www.mikrocontroller.net/links.htm ersetzen. Geplant ist dass die Leute hier ihre Links eintragen koennen, und der Inhalt dann alle paar Wochen geprueft und auf http://www.mikrocontroller.net/links.htm uebernommen wird. Was haltet ihr davon? ([[Benutzer:Andreas|Andreas]] 13:59, 19. Apr 2004 (CEST))&lt;br /&gt;
&lt;br /&gt;
Klasse Idee! Am besten dann noch mit Hinweis darauf, dass man im Wiki die aktuelle Version findet und man dort Änderungen machen kann. Das gleich könnte man ja auch mit der AVR-GCC und MSP430 Seite machen, oder? Dann wäre die verwirrende Redundanz etwas verringert - und vielleicht nutzen dann mehr Leute das Wiki. Viel Spass beim diffen  :-) (ozel)&lt;br /&gt;
&lt;br /&gt;
:Genau so ist das mit den AVR- und MSP430-Seiten auch geplant. ([[Benutzer:Andreas|Andreas]] 17:14, 19. Apr 2004 (CEST))&lt;br /&gt;
&lt;br /&gt;
Klingt ganz OK. Eine Linksamlung auf CGI Basis fände ich etwas besser, aber man kann es auch mal so probieren ([[Benutzer:Suschman|Suschman]]).&lt;br /&gt;
&lt;br /&gt;
:Dafuer irgend ein CGI-System zu nehmen finde ich unnoetig umstaendlich. ([[Benutzer:Andreas|Andreas]] 17:14, 19. Apr 2004 (CEST))&lt;br /&gt;
&lt;br /&gt;
PS: Name &amp;amp; Datum werden automatisch eingefuegt wenn ihr &amp;quot;&amp;lt;nowiki&amp;gt;~~~~&amp;lt;/nowiki&amp;gt;&amp;quot; eingebt. ([[Benutzer:Andreas|Andreas]] 17:14, 19. Apr 2004 (CEST))&lt;br /&gt;
&lt;br /&gt;
Was meint ihr, wie sollte man denn die Kategorieneinteilung machen? Einfach alles was mit AVR zu tun hat unter eine Überschrift zu schmeißen finde ich nicht sehr übersichtlich. ([[Benutzer:Andreas|Andreas]] 22:37, 28. Mai 2004 (CEST))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Link zur Seite von Peter Fleury funktioniert nicht mehr, kennt jemand die Adresse einer neuen Seite?&lt;br /&gt;
([[Benutzer:TTL|TTL]] 09:59, 3. Okt 2004 (CEST))&lt;br /&gt;
:Link aktualisiert. Google-Suche nach Peter Fleury hat geholfen =) --[[Benutzer:Oxygene|Oxygene]] 10:27, 3. Okt 2004 (CEST)&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Linksammlung&amp;diff=4424</id>
		<title>Linksammlung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Linksammlung&amp;diff=4424"/>
		<updated>2004-10-03T08:27:03Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: http://jump.to/fleury&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== [[AVR]] ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.atmel.com/products/avr/ Atmel.com] Herstellerseiten&lt;br /&gt;
* [http://www.atmel.com/dyn/general/updates.asp Atmel.com updates] Liste der letzten Änderungen in Datenblättern und Beispielcode auf ATMEL.com (nicht nur für AVRs)&lt;br /&gt;
* [http://www.avrfreaks.net/ AVR Freaks] AVR Forum, Samples, Tutorials, User-Projekte, GCC für AVR (Registrierung empfohlen)&lt;br /&gt;
* [http://www.mikrocontroller.net Mikrocontroller.net] - AVR Tutorials, Examples, LINKS, Forum (D)&lt;br /&gt;
* [http://www.mc-project.de/ mc-project] AVR Tutorial (D) &lt;br /&gt;
* [http://www.avr-asm-tutorial.net Atmel AVR Microcontroller Assembler Tutorial] (D)&lt;br /&gt;
* [http://www.avrbeginners.net AVRBeginners.net] Beginners Guides to AVRs&lt;br /&gt;
* [http://www.openavr.org/ Openavr.org] &amp;quot;central repository of information for the various open source tools available for the development of software for Atmel&#039;s AVR family of 8-bit RISC microcontrollers&amp;quot;&lt;br /&gt;
* [http://www.omegav.ntnu.no/avr/resources.php3 Omega V&#039;s AVR Resource List]&lt;br /&gt;
* [http://www.omegav.ntnu.no/avr/newresources.php3 Omega V&#039;s AVR NEW Resource List]&lt;br /&gt;
* [http://www.ipass.net/hammill/newavr.htm Atmel AVR Embedded Microcontroller Resources]&lt;br /&gt;
* [http://members.tripod.com/Stelios_Cellar/AVR/AVR%20Info.html Stelios Cellar Atmel AVR Info Page] - Samples, Links&lt;br /&gt;
* [http://sourceforge.net/projects/winavr WinAVR] (pronounced &amp;quot;whenever&amp;quot;) is a suite of executable, open source software development tools for the Atmel AVR series [for the] Windows platform&amp;quot; (includes GNU GCC) &lt;br /&gt;
* [http://www.nongnu.org/avr-libc/ avr-libc] avr-gcc&#039;s &amp;quot;standard&amp;quot;-library&lt;br /&gt;
&amp;lt;!-- * [http://hubbard.engr.scu.edu/embedded/avr/avrlib/ Procyon AVRlib] a lot of device drivers and Visual-Studio link for avr-gcc --&amp;gt;&lt;br /&gt;
* [http://procyonengineering.com/avr/avrlib/ Procyon AVRlib] a lot of device drivers and Visual-Studio link for avr-gcc&lt;br /&gt;
* [http://rod.info/avr.html rod.info on AVR] esp. for AVR GNU development tools setup under Linux &lt;br /&gt;
* [http://www.tavrasm.org/ tavrasm] = Toms Linux (Atmel) AVR Assembler &lt;br /&gt;
* [http://www.visi.com/~dwinker/revava/ revava] Disassembler&lt;br /&gt;
* [http://instruct1.cit.cornell.edu/courses/ee476/FinalProjects/ Cornell University ECE 476 Microcontroller Design Final Projects] (!)&lt;br /&gt;
* [http://jump.to/fleury Peter Fleury&#039;s Pages]  - Atmel AVR Projects, Software, Tools and Links&lt;br /&gt;
* [http://www.riccibitti.com Alberto Ricci Bitti] u.a. PAL Video-Interface&lt;br /&gt;
* [http://home.t-online.de/home/holger.klabunde/ Holgis Elektonik-Seiten] AVR/PIC Projects (D)&lt;br /&gt;
* [http://www.online-club.de/~burkhard-john/index.html Burkhard John] (D)&lt;br /&gt;
* [http://www.serasidis.gr/ Serasidis Vasilis&#039; AVRsite] u.a. GLCD, SMS, PAL&lt;br /&gt;
* [http://home.planet.nl/~meurs274/ AVRmicrocontrollerprojects] u.a. Text-LCD, Schrittmotor, Thermometer&lt;br /&gt;
* [http://www.mikrocontroller.com mikrocontroller.com] u.a. AVR-Ctrl (D)&lt;br /&gt;
* [http://users.etech.fh-hamburg.de/users/Dziedz_C/mikrocontroller/index.html Rork Xanders] Mikrocontroller Projekte (D)&lt;br /&gt;
* [http://hem.bredband.net/robinstridh/ Robin Stridh] Rotor-Anzeige, Video-Interface&lt;br /&gt;
* [http://www.dertien.dds.nl/content/avrprojects.html dertien.dds.nl AVR-Projects]&lt;br /&gt;
* [http://www.canathome.de/ Can@Home] CAN als &amp;quot;Installationsbus&amp;quot; u.a. mit AVRs (D)&lt;br /&gt;
* [http://www.embedtronics.com/ embedtronics.com]&lt;br /&gt;
* [http://www.ethernut.de Ethernut] AVR based Hardware with Ethernet-Interface, Multithreading OS, Software and Hardwaredesign is free&lt;br /&gt;
* [http://www.laskater.com/projects/uipAVR.htm TCP/IP Stack für AVR] mit Realtek RTL8019AS oder Asix AX88796 Netzwerk-Chips (open source f. avr-gcc und Imagecraft). Passende Hardware in [http://www.edtp.com/ diesem online-shop]&lt;br /&gt;
* [http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm  Igor-Plug] USB Device interface in AVR Firmware - no extra Interface IC needed, read the License &lt;br /&gt;
* [http://www.kanda.com Kanda] Starter Kits and Development Tools for different Microcontrollers&lt;br /&gt;
* [http://www.dontronics.com Dontronics] Starter Kits and Development Tools for different Microcontrollers, Linkpages for AVR and PIC&lt;br /&gt;
* [http://www.siwawi.arubi.uni-kl.de/avr_projects  M. Thomas&#039; AVR Projekte] und AVR butterfly avr-gcc-port &amp;lt;!-- Vorsicht &amp;quot;Eigenwerbung&amp;quot; --&amp;gt;&lt;br /&gt;
* [http://www.ulrichradig.de Mikrocontroller and more] AVR - Projekte (Ethernet,LCD,Relaiskarte usw) und mehr&lt;br /&gt;
&lt;br /&gt;
== [[MCS51]] ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[http://www.erikbuchmann.de/ Erik Buchmann&#039;s Mikrocontroller-Seite]&#039;&#039;&#039; - Assemblerkurs und mehrere Projekte&lt;br /&gt;
* [http://home.arcor.de/ralfhengesbach/digi/ 80C535 Assembler Programmierung]&lt;br /&gt;
* [http://home.t-online.de/home/holger.klabunde/projects/8051.htm Experimentierboard für 8051 Controller]&lt;br /&gt;
* [http://www.woe.de.vu/ World Of Electronics] - Projekte mit den 8051-Controllern von Atmel&lt;br /&gt;
* [http://www.nord-com.net/twede/Mikrocontroller/SAB80C535/sab80c535.html Controllerplatine mit SAB80C535]&lt;br /&gt;
* [http://www.nomad.ee/micros/8052bas.html 8052 BASIC Projects] - IDE-Interface&lt;br /&gt;
* [http://home.t-online.de/home/s.holst/sh51/index.html Mikrokontroller sh51] Schaltplan fuer 80C535-Board&lt;br /&gt;
* 8051-Makroassembler [http://plit.de/asem-51/ ASEM-51] (Freeware)&lt;br /&gt;
* [http://sdcc.sourceforge.net/ SDCC - Small Device C Compiler] - freier ANSI-C compiler für Intel 8051, Maxim DS80C390 und Zilog Z80 kompatible Controller.&lt;br /&gt;
&lt;br /&gt;
== [[CPLD]] / [[FPGA]] / [[GAL]] ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.opencores.org/ OpenCores.org, VHDL Sourcen]&lt;br /&gt;
* [http://www.fpga4fun.com/ fpga4fun]&lt;br /&gt;
&lt;br /&gt;
== Vermischtes == &lt;br /&gt;
&lt;br /&gt;
* [http://www.epanorama.net ePanorama.net]&lt;br /&gt;
* [http://www.commlinx.com.au/schematics.htm Electronic Schematics] from CommLinx Solutions Pty Ltd&lt;br /&gt;
* [http://www.discovercircuits.com Discover Circuits] a collection of 6000+ electronic circuits or schematics&lt;br /&gt;
* [http://www.mathar.com MSP430 Tutorials] Tutorials, Anleitungen und viele Beispielprojekte mit dem MSP430-Mikrocontroller&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4370</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=4370"/>
		<updated>2004-10-01T23:01:29Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: komma gesetzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Vorwort =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll Einsteigern helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039; die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
VERALTET&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die ursprüngliche Version stammt von Christian Schifferle, die meisten aktuellen Anpassungen von [[Benutzer:Mthomas]]. Viele der im Original-Dokument verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als überholt (&#039;&#039;deprecated&#039;&#039;) ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst. &lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert.&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers Ihrer Wahl. Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. &amp;lt;!-- veraltet: Dieser Controller weist meiner Meinung nach das beste Preis-/Leistungsverhältnis auf. --&amp;gt;&lt;br /&gt;
* AVRGCC-Compiler&amp;lt;br /&amp;gt; Ich beschränke mich hier auf den GCC Compiler, weil ich diesen selber verwende und weil er kostenlos zu haben und weit verbreitet ist. Siehe auch: [[AVR-GCC]]&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
&amp;lt;!--, und natürlich ein passendes Kabel, um die Programme auf den Atmel übertragen zu können.--&amp;gt;&lt;br /&gt;
* Wenn man debuggen will eventuell [[AVR-Studio]]&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Einträge schon im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingetragen.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Auschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[avrdude]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU ensprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon eingetragen. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware avrdude angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.???) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 1.84467440737E19.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-Wort (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein Byte (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef signed char int8_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
typedef unsigned char uint8_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef int int16_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
typedef unsigned int uint16_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef long int32_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
typedef unsigned long uint32_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef long long int64_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
typedef unsigned long long uint64_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef int16_t intptr_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef uint16_t uintptr_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen&lt;br /&gt;
den Eingangspin des Controllers und Masse geschaltet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die&lt;br /&gt;
Versorgungsspannung und Masse geschaltet.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten,&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehr als zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART RX Complete Interrupt&lt;br /&gt;
ausgelöst wenn ein Zeichen vom UART empfangen wurde. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART TX Complete Interrupt&lt;br /&gt;
ausgelöst wenn ein Zeichen vom UART gesendet wurde. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART Datenregister Leer&lt;br /&gt;
Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues&lt;br /&gt;
zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt&lt;br /&gt;
Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Nur wenn dieses Bit gesetzt ist arbeitet der Empfänger des UART&lt;br /&gt;
überhaupt. Wenn das Bit nicht gesetzt ist kann der entsprechende&lt;br /&gt;
Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Nur wenn dieses Bit gesetzt ist arbeitet der Sender des UART&lt;br /&gt;
überhaupt. Wenn das Bit nicht gesetzt ist kann der entsprechende&lt;br /&gt;
Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| 9 Bit Characters&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist können 9 Bit lange Zeichen übertragen&lt;br /&gt;
und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches&lt;br /&gt;
Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von&lt;br /&gt;
einem 11-Bit Zeichenrahmen:&amp;lt;br /&amp;gt;&lt;br /&gt;
1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| Receive Data Bit 8&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses&lt;br /&gt;
Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
| Transmit Data Bit 8&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses&lt;br /&gt;
Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor&lt;br /&gt;
das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;amp;amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| UART Receive Complete&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom&lt;br /&gt;
Empfangs-Schieberegister in das Empfangs-Datenregister transferiert&lt;br /&gt;
wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Zeichen muss nun schnellstmöglich aus dem Datenregister&lt;br /&gt;
ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres&lt;br /&gt;
Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation&lt;br /&gt;
eintreffen. Mit dem Auslesen des Datenregisters wird das Bit&lt;br /&gt;
automatisch gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| UART Transmit Complete&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister&lt;br /&gt;
befindliche Zeichen vollständig ausgegeben wurde und kein weiteres&lt;br /&gt;
Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die&lt;br /&gt;
Kommunikation vollumfänglich abgeschlossen ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das&lt;br /&gt;
Programm nach dem Senden von Daten auf Empfang schalten muss. Im&lt;br /&gt;
Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit nur dann automatisch gelöscht, wenn der entsprechende&lt;br /&gt;
Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit&lt;br /&gt;
selber löschen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom&lt;br /&gt;
Sendedatenregister in das Send-Schieberegister übernommen wurde und&lt;br /&gt;
der UART nun wieder bereit ist, ein neues Zeichen zum Senden&lt;br /&gt;
aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn ein Zeichen in das&lt;br /&gt;
Sendedatenregister geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn der UART einen&lt;br /&gt;
Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines&lt;br /&gt;
empfangenen Zeichens 0 ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen&lt;br /&gt;
Zeichens 1 ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im&lt;br /&gt;
Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das&lt;br /&gt;
nachfolgende Zeichen komplett empfangen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das nachfolgende Zeichen wird verworfen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in&lt;br /&gt;
das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
UBRR = Taktfrequenz / (Baudrate * 16) - 1&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funkstionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter&lt;br /&gt;
Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäss unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 4000000; // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600 // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;#define F_CPU 4000000&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
// Zum Beispiel 4Mhz-Quarz&amp;lt;br /&amp;gt;&lt;br /&gt;
#define UART_BAUD_RATE 9600 // Wir versuchen mal mit 9600 Baud&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (F_CPU / (UART_BAUD_RATE * 16L) - 1, UBRR);&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;aten &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (&#039;x&#039;, UDR);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Schreibt das Zeichen x auf die Schnittstelle&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf++) {&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist jetzt insofern etwas kritisch, dass wir während dem Senden des&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren können. Dies wird aber&lt;br /&gt;
ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/pfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039; DAC&#039;&#039;&#039; oder &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann wird durch die Auflösung des &#039;&#039;&#039; ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039; ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/255 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375,&amp;amp;nbsp; 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird entladen (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die&amp;amp;nbsp; Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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 Mass 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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren.&lt;br /&gt;
Wenn das Bit nicht gesetzt ist können die Pin&#039;s wie normale&lt;br /&gt;
I/O-Pins verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden&lt;br /&gt;
Betriebsart muss das Bit gesetzt werden, um die kontinuierliche&lt;br /&gt;
Messung zu aktivieren.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Bit&amp;amp;nbsp; nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal&lt;br /&gt;
gesetzt wird führt der Controller zuerst eine zusätzliche Wandlung&lt;br /&gt;
und erst dann die eigentliche Wandlung aus. Diese zusätzliche&lt;br /&gt;
Wandlung wird zu Initialisierungszwecken durchgeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen&lt;br /&gt;
ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung&lt;br /&gt;
erfolgt ist und geht danach auf 0.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Fr&#039;&#039;&#039;ee Running Select&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird die Betriebsart eingestellt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst&lt;br /&gt;
nun ständig den ausgewählten Kanal und schreibt den gemessenen&lt;br /&gt;
Wert in das &#039;&#039;&#039; ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt wenn eine Umwandlung erfolgt und das&lt;br /&gt;
&#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
wird der &#039;&#039;&#039; ADC Interrupt&#039;&#039;&#039; ausgelöst und die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen..&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht wenn die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch&lt;br /&gt;
gelöscht werden, indem ein logisches 1 in das Register geschrieben&lt;br /&gt;
wird (So steht&#039;s in der AVR-Doku).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister&lt;br /&gt;
&#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&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
ADPS0&#039;&#039;&#039;&lt;br /&gt;
| &#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&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz&lt;br /&gt;
und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der&lt;br /&gt;
CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz&lt;br /&gt;
sein.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorteiler muss also so eingestellt werden, dass die&lt;br /&gt;
CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert&lt;br /&gt;
zwischen 50-200kHz ergibt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmin=CLK/200kHz=4000000/200000=&#039;&#039;&#039;20&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmax=CLK/50kHz=4000000/50000=&#039;&#039;&#039;80&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im&lt;br /&gt;
Interesse der schnelleren Wandlungszeit werden wir hier den Faktor&lt;br /&gt;
32 einstellen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MUX2&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
MUX0&#039;&#039;&#039;&lt;br /&gt;
| Mit diesem 3 Bits wird der zu messende Kanal bestimmt.&lt;br /&gt;
Es wird einfach die entsprechende Pinnummer des Ports&lt;br /&gt;
eingeschrieben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Register beschrieben wird, während dem eine Umwandlung&lt;br /&gt;
läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen&lt;br /&gt;
Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu&lt;br /&gt;
berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Meine Empfehlung ist deswegen klar diese, dass der frei laufende&lt;br /&gt;
Betrieb nur bei einem einzelnen zu verwendenden Analogeingang&lt;br /&gt;
verwendet werden sollte, wenn man sich Probleme bei der Umschalterei&lt;br /&gt;
ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);     //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel; // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);        // eine ADC-Wandlung &lt;br /&gt;
  while(!(ADCSRA &amp;amp; 0x10));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
	result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);      // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Hinweis:&lt;br /&gt;
| In den folgenden Überlegungen wird als Controller der&lt;br /&gt;
90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen&lt;br /&gt;
AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039; PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039; PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: fix --&amp;gt;&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp&lt;br /&gt;
((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;!-- TODO: fix, irgenwas ist bei der &amp;quot;wiki-konvertierung&amp;quot; wohl schief gelaufen --&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1&amp;lt;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&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;
Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller raus lesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS02&amp;lt;br /&amp;gt;&lt;br /&gt;
CS01&amp;lt;br /&amp;gt;&lt;br /&gt;
CS00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird&lt;br /&gt;
ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang&lt;br /&gt;
geschaltet ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen, schreiben wir die folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
TCCR0 = (1&amp;lt;&amp;lt;CS00)|(1&amp;lt;&amp;lt;CS02);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun aufwärts bis 255, um dann wieder bei 0 zu beginnen. Der aktuelle Zählerstand steht in TCNT0. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow Flag &#039;&#039;&#039;TOV0&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;TIFR&#039;&#039;&#039;-Register gesetzt und, falls so konfiguriert, ein entsprechender Timer-Overflow-Interrupt ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATMega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* Die [[PWM]]-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals. &lt;br /&gt;
* Vergleichswert-Überprüfung mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* Einfangen eines Eingangssignals mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits&lt;br /&gt;
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter 1 den Wert des Vergleichsregisters erreicht, also ein so genannter Compare Match auftritt.&lt;br /&gt;
Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert&lt;br /&gt;
(Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
| In der PWM-Betriebsart haben diese Bits eine andere Funktion.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 1 gesetzt.&lt;br /&gt;
Man nennt dies nicht invertierende PWM.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1 gesetzt.&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister erreicht so wird der Pin auf 0 gesetzt.&lt;br /&gt;
Man nennt dies invertierende PWM.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PWM11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1 gesteuert.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter 1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler&lt;br /&gt;
(4 CKs) Timer/Counter 1&amp;lt;br /&amp;gt;&lt;br /&gt;
oder auf Deutsch Rauschunterdrückung des Eingangssignals.&lt;br /&gt;
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal gearbeitet wird so werden nach der Triggerung des Signals mit der entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand aufweisen gilt das Signal als erkannt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1)&lt;br /&gt;
oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input&lt;br /&gt;
Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter on Compare Match Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; auf 0 gesetzt.&lt;br /&gt;
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird ergibt sich je nach eingestelltem Vorzähler ein etwas anderes Zählverhalten:&lt;br /&gt;
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt betrachtet, folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2 | C-1 | C | 0 | 1 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das Datenregister folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1,&lt;br /&gt;
C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS12&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein&lt;br /&gt;
Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet&lt;br /&gt;
ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet, d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0 erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte überein so wird ein sogenannter Output Compare Match ausgelöst. Die entsprechenden Aktionen werden über die Timer/Counter 1 Control und Status Register eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039; oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäss Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039; definierte Flanke erkannt wird so wird der aktuelle Inhalt des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler&lt;br /&gt;
betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach wieder zurück auf 0.&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
gespeicherten Wert erreicht wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw.&lt;br /&gt;
gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik&lt;br /&gt;
zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;) ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert verglichen wird.&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers eine Signalquelle angeschlossen.&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1 (steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann, wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein&lt;br /&gt;
Vergleichswert definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes&lt;br /&gt;
ein Compare Match Interrupt ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt&lt;br /&gt;
ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP)&lt;br /&gt;
auftritt. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein,&lt;br /&gt;
wenn auch ein entsprechender Interrupt ausgelöst werden soll.&amp;lt;font face=&amp;quot;Helvetica&amp;quot; size=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung&lt;br /&gt;
von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters&lt;br /&gt;
von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
übereinstimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist,&lt;br /&gt;
welches anzeigt, dass der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 in das Input Capture Register ICR1 übertragen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039; deklarierte Variable niemals kleiner als Null werden kann (der Compiler sollte jedoch eine ensprechende Warnung ausgeben).&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1 steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| 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;&lt;br /&gt;
| 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;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| 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-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird. Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine beendet ist.&lt;br /&gt;
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;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#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&lt;br /&gt;
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;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&lt;br /&gt;
Dieses Bit bestimmt der 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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;&amp;lt;br /&amp;gt;&#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
In der Beschreibung heisst es, der Interrupt wird getriggert, solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank 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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich können alle Interruptspezifischen Registerzugriffe wie&lt;br /&gt;
gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es&lt;br /&gt;
jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei&lt;br /&gt;
interrupt.h einbinden mittels&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und dann kann&#039;s losgehen&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind braucht es selbstverständlich noch&lt;br /&gt;
den auszuführenden Code, der ablaufen soll wenn ein Interrupt eintrifft.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu gibt es zwei Definitionen welche allerdings AVR-GCC spezifisch sind und&lt;br /&gt;
bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit SIGNAL wird eine Funktion für die Bearbeitung eines Interrupts&lt;br /&gt;
eingeleitet. Als Argument muss dabei die Benennung des entsprechenden&lt;br /&gt;
Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien&lt;br /&gt;
IOxxxx.h zu finden. Mögliche Funktionsrümpfe für solche Interruptfunktionen&lt;br /&gt;
sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des Funktion sind alle weiteren Interrupts&lt;br /&gt;
automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder&lt;br /&gt;
zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt&lt;br /&gt;
(gleiche oder andere Interruptquelle) auftreten so wird das entsprechende Bit im&lt;br /&gt;
zugeordneten Interrupt Flag Register gesetzt und die entsprechende&lt;br /&gt;
Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der&lt;br /&gt;
aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die&lt;br /&gt;
entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen&lt;br /&gt;
wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch&lt;br /&gt;
öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass&lt;br /&gt;
Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden&lt;br /&gt;
sollten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied&lt;br /&gt;
ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global&lt;br /&gt;
Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts&lt;br /&gt;
zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten&lt;br /&gt;
Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und&lt;br /&gt;
sollte wirklich nur dann angewendet werden wenn man sich absolut sicher ist, das&lt;br /&gt;
Ganze auch im Griff zu haben.&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Floats und Structs lesen ===&lt;br /&gt;
&lt;br /&gt;
Um komplexe Datentypen (structs), nicht-integer Datentypen (floats) aus dem Flash auszulesen, sind Hilfsfunktionen erforderlich. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Beispiel float aus Flash */&lt;br /&gt;
&lt;br /&gt;
float pgmFloatArray[3] PROGMEM = {1.1, 2.2, 3.3};&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* liest float von Flash-Addresse addr und gibt diese als return-value zurueck */&lt;br /&gt;
inline float pgm_read_float(const float *addr)&lt;br /&gt;
{	&lt;br /&gt;
	union&lt;br /&gt;
	{&lt;br /&gt;
		uint16_t i[2];	// 2 16-bit-Worte&lt;br /&gt;
		float f;&lt;br /&gt;
	} u;&lt;br /&gt;
	&lt;br /&gt;
	u.i[0]=pgm_read_word((PGM_P)addr);&lt;br /&gt;
	u.i[1]=pgm_read_word((PGM_P)addr+2);&lt;br /&gt;
	&lt;br /&gt;
	return u.f;&lt;br /&gt;
} &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   float f;&lt;br /&gt;
&lt;br /&gt;
   for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
      f = pgm_read_float(&amp;amp;pgmFloatArray[i]); // entspr. &amp;quot;f = pgmFloatArray[i];&amp;quot;&lt;br /&gt;
      // mach&#039; was mit f &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele fuer structs und pointer aus flash auf struct im flash (menues, state-machines etc.)&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Konstanten&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01fe), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich zu jedem Pointer den Ablageort (Flash oder RAM) intern sichern. Bei Aufruf einer Funktion wird dann bei Pointer-Parametern neben der Adresse auch der Speicherbereich, auf den der Pointer zeigt, übergeben. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer Präprozessor-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* float */&lt;br /&gt;
float eeFooFloat EEPROM;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* 16-bit unsigned short feld */&lt;br /&gt;
 &amp;lt;!-- #define MAXELEM wo wird das hier verwendet? hab ich was übersehen? Nein, keine Ahnung warum ich das da rein geschreiben hatte. --&amp;gt;&lt;br /&gt;
uint16_t eeFooWordArray1[4] EEPROM;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&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;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&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 &#039;&#039;eeprom_read_block()&#039;&#039; bzw. &#039;&#039;eeprom_write_block()&#039;&#039;. Die Funktionen erwarten drei Parameter: die Adresse der Quell- bzw. Zieldaten im RAM, die EEPROM-Addresse und die Länge des Datenblocks in Bytes (size_t).&lt;br /&gt;
&lt;br /&gt;
TODO: &#039;&#039;&#039;Vorsicht!&#039;&#039;&#039; die folgenden Beispiele sind noch nicht geprueft, erstmal nur als Hinweis auf &amp;quot;das Prinzip&amp;quot;. Evtl. fehlen &amp;quot;casts&amp;quot; und mglw. noch mehr.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    unit8_t  myByteBuffer[3];&lt;br /&gt;
    uint16_t myWordBuffer[4];&lt;br /&gt;
&lt;br /&gt;
    /* Datenblock aus EEPROM LESEN  */&lt;br /&gt;
&lt;br /&gt;
    /* liest 3 Bytes aber 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 etwas anschaulicher aber &amp;quot;unnuetze Tipparbeit&amp;quot;: */&lt;br /&gt;
    eeprom_read_block(&amp;amp;myByteBuffer[0],&amp;amp;eeFooByteArray[0],3);&lt;br /&gt;
&lt;br /&gt;
    /* dito mit etwas Absicherung betr. der Laenge */&lt;br /&gt;
    eeprom_read_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* und nun mit &amp;quot;16bit&amp;quot; */&lt;br /&gt;
    eeprom_read_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
&lt;br /&gt;
    /* Datenlock in EEPROM SCHREIBEN */&lt;br /&gt;
    eeprom_write_block(myByteBuffer,eeFooByteArray1,sizeof(myByteBuffer));&lt;br /&gt;
    eeprom_write_block(myWordBuffer,eeFooWordArray,sizeof(myWordBuffer));&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Nicht-Integer&amp;quot;-Datentypen wie z.B. Fliesskommazahlen lassen sich recht praktisch ueber eine &#039;&#039;union&#039;&#039; in &amp;quot;Byte-Arrays&amp;quot; konvertieren und wieder &amp;quot;zurückwandeln&amp;quot;. Dies erweist sich hier (aber nicht nur hier) als nützlich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
   float myFloat = 12.34;&lt;br /&gt;
&lt;br /&gt;
   union {&lt;br /&gt;
      float r;&lt;br /&gt;
      uint8_t n[sizeof(float)];&lt;br /&gt;
   } u;&lt;br /&gt;
&lt;br /&gt;
   u.r = myFloat;&lt;br /&gt;
   &lt;br /&gt;
   /* float in EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
&lt;br /&gt;
   /* float aus EEPROM */&lt;br /&gt;
   eeprom_read_block(&amp;amp;(u.i),&amp;amp;eeFooFloat,sizeof(float));&lt;br /&gt;
   /* u.r wieder 12.34 */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch zusammengesetzte Typen lassen sich mit den Block-Routinen verarbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint8_t   label[8];&lt;br /&gt;
    unin8_t   rom_code[8];&lt;br /&gt;
} tMyStruct;&lt;br /&gt;
&lt;br /&gt;
#define MAXSENSORS 3&lt;br /&gt;
tMyStruct eeMyStruct[MAXSENSORS] EEPROM;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
void egal(void)&lt;br /&gt;
{&lt;br /&gt;
   tMyStruct work;&lt;br /&gt;
   &lt;br /&gt;
   strcpy(work.label,&amp;quot;Flur&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);     // Dummy zur Veranschaulichung - setzt rom-code&lt;br /&gt;
&lt;br /&gt;
   /* Sichern von &amp;quot;work&amp;quot; im EEPROM */&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct)); // f. Index 0&lt;br /&gt;
   strcpy(work.label,&amp;quot;Bad&amp;quot;);&lt;br /&gt;
   GetRomCode(work.rom_code);&lt;br /&gt;
   eeprom_write_block(&amp;amp;work,&amp;amp;eeMyStruct[1],sizeof(tMyStruct)); // f. Index 1&lt;br /&gt;
...&lt;br /&gt;
   /* Lesen der Daten EEPROM Index 0 in &amp;quot;work&amp;quot; */&lt;br /&gt;
   eeprom_read_block(&amp;amp;work,&amp;amp;eeMyStruct[0],sizeof(tMyStruct));&lt;br /&gt;
   // work.label hat nun den Inhalt &amp;quot;Flur&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem WinAVR-Beispielmakefile ersichtlich. Siehe dazu die Erläuterungen im Abschnitt Exkurs: Makefiles.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien). &lt;br /&gt;
&lt;br /&gt;
In jedem Datenblatt zu AVR-Controllern mit EEPROM sind kurze Beispielecodes für den Schreib- und Lesezugriff enthalten. Der dort gezeigt Code kann direkt auch mit dem avr-gcc (ohne avr-libc/eeprom.h) genutzt werden (&amp;quot;copy/paste&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
=Externe Baugrupen =&lt;br /&gt;
==I2C==&lt;br /&gt;
===Master===&lt;br /&gt;
[http://homepage.sunrise.ch/mysunrise/pfleury/group__pfleury__ic2master.html I2C Master library]&lt;br /&gt;
===Slave===&lt;br /&gt;
==CAN==&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== HD44870===&lt;br /&gt;
[http://homepage.sunrise.ch/mysunrise/pfleury/group__pfleury__lcd.html LCD]&lt;br /&gt;
=== Nokia3310===&lt;br /&gt;
=== Nokia 6100 LCD===&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4344</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=4344"/>
		<updated>2004-09-30T10:50:42Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: /* Standardisierte Typen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Programmierung der AVR-Mikrocontroller mit C =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll dem Einsteiger helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039;&lt;br /&gt;
die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es gibt einige Gründe, weshalb eine höhere Programmiersprache der reinen&lt;br /&gt;
Assembler-Programmierung vorgezogen werden kann. So kann mit &#039;&#039;&#039;C&#039;&#039;&#039; das&lt;br /&gt;
Programm wesentlich lesbarer geschrieben werden als in Assembler.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es soll allerdings auch nicht verheimlicht werden, dass in Assembler in der&lt;br /&gt;
Regel schnellerer Programmcode entwickelt werden kann als mit &#039;&#039;&#039;C&#039;&#039;&#039;. Meiner&lt;br /&gt;
Meinung nach jedoch können wir 99 Prozent aller Aufgaben problemlos mit &#039;&#039;&#039;C&#039;&#039;&#039;&lt;br /&gt;
meistern und für die ganz krassen Fälle kann sogar Assembler-Code direkt in&lt;br /&gt;
ein &#039;&#039;&#039;C&#039;&#039;&#039;-Programm eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Der Autor hat sich alle Mühe gegeben, sein Wissen hier fehlerfrei wiederzugeben. Fehler können jedoch nicht ausgeschlossen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Fehlermeldungen bitte an christian.schifferle@bluewin.ch&lt;br /&gt;
&lt;br /&gt;
Der Autor übernimmt keinerlei Haftung für etwaige Schäden wie&lt;br /&gt;
z.B. durchgeknallte Sicherungen,&lt;br /&gt;
welche durch die Verwendung dieses Dokuments entstehen könnten.&lt;br /&gt;
&lt;br /&gt;
Die deutschen Leser mögen mir verzeihen, dass ich, wie es in&lt;br /&gt;
der Schweiz üblich ist, kein scharfes &amp;amp;szlig; verwendet habe.&lt;br /&gt;
&lt;br /&gt;
Die vorliegende Dokumentation ist und bleibt geistiges Machwerk&lt;br /&gt;
von Christian Schifferle und darf nicht als Eigenproduktion angepriesen werden.&lt;br /&gt;
&lt;br /&gt;
Das Dokument darf nach Belieben an Dritte weiter gegeben werden,&lt;br /&gt;
sofern der Copyright-Hinweis auf den Autor nicht verändert oder gelöscht wird.&lt;br /&gt;
&lt;br /&gt;
Der Autor, nämlich ich, das ist:&lt;br /&gt;
&lt;br /&gt;
Christian Schifferle&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Risweg 7&amp;lt;br /&amp;gt;&lt;br /&gt;
CH-4624 Härkingen&amp;lt;br /&amp;gt;&lt;br /&gt;
E-Mail: [mailto:christian.schifferle@bluewin.ch christian.schifferle@bluewin.ch]&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Aktualisierung =&lt;br /&gt;
&lt;br /&gt;
Viele der im Original-Dokument von C. Schifferle verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als deprecated ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst und stimmt somit nicht mehr mit den Informationen im zip-Achiv überein.&lt;br /&gt;
Einige Textpassagen entsprechen nicht mehr dem Originaldokument, einige Codebeispiele wurden hinzugefügt oder erweitert, der Abschnitt &amp;quot;Speicher&amp;quot; ist neu.&lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert. &lt;br /&gt;
&lt;br /&gt;
(M. Thomas Sept./04)&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers Ihrer Wahl. Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. &amp;lt;!-- veraltet: Dieser Controller weist meiner Meinung nach das beste Preis-/Leistungsverhältnis auf. --&amp;gt;&lt;br /&gt;
* AVRGCC-Compiler&amp;lt;br /&amp;gt; Ich beschränke mich hier auf den GCC Compiler, weil ich diesen selber verwende und weil er kostenlos zu haben und weit verbreitet ist. Siehe auch: [[AVR-GCC]]&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
&amp;lt;!--, und natürlich ein passendes Kabel, um die Programme auf den Atmel übertragen zu können.--&amp;gt;&lt;br /&gt;
* Wenn man debuggen will eventuell [[AVR-Studio]]&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Einträge schon im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingetragen.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Auschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[avrdude]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU ensprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon eingetragen. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware avrdude angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.???) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 1.84467440737E19.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierten Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR &#039;&#039;uint64_t&#039;&#039;, ein Doppel-Wort (32bit) entspricht &#039;&#039;uint32_t&#039;&#039;, ein Wort (16bit) entspricht &#039;&#039;uint16_t&#039;&#039; und ein Byte (8bit) entspricht &#039;&#039;uint8_t&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef signed char int8_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
typedef unsigned char uint8_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef int int16_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
typedef unsigned int uint16_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef long int32_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
typedef unsigned long uint32_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef long long int64_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
typedef unsigned long long uint64_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef int16_t intptr_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef uint16_t uintptr_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen&lt;br /&gt;
den Eingangspin des Controllers und Masse geschaltet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die&lt;br /&gt;
Versorgungsspannung und Masse geschaltet.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten,&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehr als zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART RX Complete Interrupt&lt;br /&gt;
ausgelöst wenn ein Zeichen vom UART empfangen wurde. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART TX Complete Interrupt&lt;br /&gt;
ausgelöst wenn ein Zeichen vom UART gesendet wurde. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART Datenregister Leer&lt;br /&gt;
Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues&lt;br /&gt;
zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt&lt;br /&gt;
Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Nur wenn dieses Bit gesetzt ist arbeitet der Empfänger des UART&lt;br /&gt;
überhaupt. Wenn das Bit nicht gesetzt ist kann der entsprechende&lt;br /&gt;
Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Nur wenn dieses Bit gesetzt ist arbeitet der Sender des UART&lt;br /&gt;
überhaupt. Wenn das Bit nicht gesetzt ist kann der entsprechende&lt;br /&gt;
Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| 9 Bit Characters&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist können 9 Bit lange Zeichen übertragen&lt;br /&gt;
und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches&lt;br /&gt;
Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von&lt;br /&gt;
einem 11-Bit Zeichenrahmen:&amp;lt;br /&amp;gt;&lt;br /&gt;
1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| Receive Data Bit 8&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses&lt;br /&gt;
Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
| Transmit Data Bit 8&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses&lt;br /&gt;
Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor&lt;br /&gt;
das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;amp;amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| UART Receive Complete&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom&lt;br /&gt;
Empfangs-Schieberegister in das Empfangs-Datenregister transferiert&lt;br /&gt;
wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Zeichen muss nun schnellstmöglich aus dem Datenregister&lt;br /&gt;
ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres&lt;br /&gt;
Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation&lt;br /&gt;
eintreffen. Mit dem Auslesen des Datenregisters wird das Bit&lt;br /&gt;
automatisch gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| UART Transmit Complete&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister&lt;br /&gt;
befindliche Zeichen vollständig ausgegeben wurde und kein weiteres&lt;br /&gt;
Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die&lt;br /&gt;
Kommunikation vollumfänglich abgeschlossen ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das&lt;br /&gt;
Programm nach dem Senden von Daten auf Empfang schalten muss. Im&lt;br /&gt;
Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit nur dann automatisch gelöscht, wenn der entsprechende&lt;br /&gt;
Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit&lt;br /&gt;
selber löschen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom&lt;br /&gt;
Sendedatenregister in das Send-Schieberegister übernommen wurde und&lt;br /&gt;
der UART nun wieder bereit ist, ein neues Zeichen zum Senden&lt;br /&gt;
aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn ein Zeichen in das&lt;br /&gt;
Sendedatenregister geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn der UART einen&lt;br /&gt;
Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines&lt;br /&gt;
empfangenen Zeichens 0 ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen&lt;br /&gt;
Zeichens 1 ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im&lt;br /&gt;
Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das&lt;br /&gt;
nachfolgende Zeichen komplett empfangen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das nachfolgende Zeichen wird verworfen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in&lt;br /&gt;
das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
UBRR = Taktfrequenz / (Baudrate * 16) - 1&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funkstionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter&lt;br /&gt;
Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäss unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 4000000; // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600 // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;#define F_CPU 4000000&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
// Zum Beispiel 4Mhz-Quarz&amp;lt;br /&amp;gt;&lt;br /&gt;
#define UART_BAUD_RATE 9600 // Wir versuchen mal mit 9600 Baud&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (F_CPU / (UART_BAUD_RATE * 16L) - 1, UBRR);&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;aten &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (&#039;x&#039;, UDR);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Schreibt das Zeichen x auf die Schnittstelle&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf++) {&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist jetzt insofern etwas kritisch, dass wir während dem Senden des&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren können. Dies wird aber&lt;br /&gt;
ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/pfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039; DAC&#039;&#039;&#039; oder &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann wird durch die Auflösung des &#039;&#039;&#039; ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039; ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/255 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375,&amp;amp;nbsp; 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird entladen (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die&amp;amp;nbsp; Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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 Mass 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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren.&lt;br /&gt;
Wenn das Bit nicht gesetzt ist können die Pin&#039;s wie normale&lt;br /&gt;
I/O-Pins verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden&lt;br /&gt;
Betriebsart muss das Bit gesetzt werden, um die kontinuierliche&lt;br /&gt;
Messung zu aktivieren.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Bit&amp;amp;nbsp; nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal&lt;br /&gt;
gesetzt wird führt der Controller zuerst eine zusätzliche Wandlung&lt;br /&gt;
und erst dann die eigentliche Wandlung aus. Diese zusätzliche&lt;br /&gt;
Wandlung wird zu Initialisierungszwecken durchgeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen&lt;br /&gt;
ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung&lt;br /&gt;
erfolgt ist und geht danach auf 0.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Fr&#039;&#039;&#039;ee Running Select&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird die Betriebsart eingestellt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst&lt;br /&gt;
nun ständig den ausgewählten Kanal und schreibt den gemessenen&lt;br /&gt;
Wert in das &#039;&#039;&#039; ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt wenn eine Umwandlung erfolgt und das&lt;br /&gt;
&#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
wird der &#039;&#039;&#039; ADC Interrupt&#039;&#039;&#039; ausgelöst und die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen..&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht wenn die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch&lt;br /&gt;
gelöscht werden, indem ein logisches 1 in das Register geschrieben&lt;br /&gt;
wird (So steht&#039;s in der AVR-Doku).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister&lt;br /&gt;
&#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&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
ADPS0&#039;&#039;&#039;&lt;br /&gt;
| &#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&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz&lt;br /&gt;
und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der&lt;br /&gt;
CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz&lt;br /&gt;
sein.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorteiler muss also so eingestellt werden, dass die&lt;br /&gt;
CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert&lt;br /&gt;
zwischen 50-200kHz ergibt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmin=CLK/200kHz=4000000/200000=&#039;&#039;&#039;20&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmax=CLK/50kHz=4000000/50000=&#039;&#039;&#039;80&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im&lt;br /&gt;
Interesse der schnelleren Wandlungszeit werden wir hier den Faktor&lt;br /&gt;
32 einstellen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MUX2&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
MUX0&#039;&#039;&#039;&lt;br /&gt;
| Mit diesem 3 Bits wird der zu messende Kanal bestimmt.&lt;br /&gt;
Es wird einfach die entsprechende Pinnummer des Ports&lt;br /&gt;
eingeschrieben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Register beschrieben wird, während dem eine Umwandlung&lt;br /&gt;
läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen&lt;br /&gt;
Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu&lt;br /&gt;
berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Meine Empfehlung ist deswegen klar diese, dass der frei laufende&lt;br /&gt;
Betrieb nur bei einem einzelnen zu verwendenden Analogeingang&lt;br /&gt;
verwendet werden sollte, wenn man sich Probleme bei der Umschalterei&lt;br /&gt;
ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);     //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel; // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);        // eine ADC-Wandlung &lt;br /&gt;
  while(!(ADCSRA &amp;amp; 0x10));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
	result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);      // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Hinweis:&lt;br /&gt;
| In den folgenden Überlegungen wird als Controller der&lt;br /&gt;
90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen&lt;br /&gt;
AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039; PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039; PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: fix --&amp;gt;&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp&lt;br /&gt;
((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;!-- TODO: fix, irgenwas ist bei der &amp;quot;wiki-konvertierung&amp;quot; wohl schief gelaufen --&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1&amp;lt;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&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;
Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller raus lesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS02&amp;lt;br /&amp;gt;&lt;br /&gt;
CS01&amp;lt;br /&amp;gt;&lt;br /&gt;
CS00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird&lt;br /&gt;
ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang&lt;br /&gt;
geschaltet ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen schreiben wir die folgende&lt;br /&gt;
Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: fix --&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1&amp;lt;&amp;lt;CS02) | (1&amp;lt;&amp;lt;CS00), TCCR0);&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun von 0 an aufwärts bis 255 um dann wieder&lt;br /&gt;
bei 0 zu beginnen. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow&lt;br /&gt;
Flag &#039;&#039;&#039;[http://en.wikipedia.org#TOV0 TOV0]&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;[http://en.wikipedia.org#TIFR TIFR]&#039;&#039;&#039;&lt;br /&gt;
Register gesetzt und, falls so konfiguriert, ein entsprechender Interrupt&lt;br /&gt;
ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATMega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* [http://en.wikipedia.org#Die%20PWM-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals] ([http://en.wikipedia.org/Analoge_Ein_Ausgabe.htm#PWM PWM]).&lt;br /&gt;
* [http://en.wikipedia.org#Vergleichswert-%DCberpr%FCfung Vergleichswert-Überprüfung] mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* [http://en.wikipedia.org#Einfangen%20eines%20Eingangssignals Einfangen eines Eingangssignals] mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;COM1A1&amp;lt;br /&amp;gt;&lt;br /&gt;
COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039;&lt;br /&gt;
ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 den Wert des Vergleichsregisters erreicht, also ein so genannter&lt;br /&gt;
Compare Match auftritt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem&lt;br /&gt;
Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert&lt;br /&gt;
(Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
| In der PWM-Betriebsart haben diese Bits eine andere&lt;br /&gt;
Funktion.&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0&lt;br /&gt;
gesetzt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister&lt;br /&gt;
erreicht so wird der Pin auf 1 gesetzt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man nennt dies nicht invertierende PWM.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1&lt;br /&gt;
gesetzt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister&lt;br /&gt;
erreicht so wird der Pin auf 0 gesetzt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man nennt dies invertierende PWM.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PWM11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1&lt;br /&gt;
gesteuert.&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter&lt;br /&gt;
1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039;&lt;br /&gt;
Timer &#039;&#039;&#039;1&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler&lt;br /&gt;
(4 CKs) Timer/Counter 1&amp;lt;br /&amp;gt;&lt;br /&gt;
oder auf Deutsch Rauschunterdrückung des Eingangssignals.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal&lt;br /&gt;
gearbeitet wird so werden nach der Triggerung des Signals mit der&lt;br /&gt;
entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039;&lt;br /&gt;
jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals&lt;br /&gt;
abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand&lt;br /&gt;
aufweisen gilt das Signal als erkannt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1)&lt;br /&gt;
oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input&lt;br /&gt;
Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter&lt;br /&gt;
on Compare Match Timer/Counter &#039;&#039;&#039;1&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung&lt;br /&gt;
des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem&lt;br /&gt;
Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
auf 0 gesetzt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird&lt;br /&gt;
ergibt sich je nach eingestelltem Vorzähler ein etwas anderes&lt;br /&gt;
Zählverhalten:&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige&lt;br /&gt;
Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt&lt;br /&gt;
betrachtet, folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2 | C-1 | C | 0 | 1 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das&lt;br /&gt;
Datenregister folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1,&lt;br /&gt;
C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS12&amp;lt;br /&amp;gt;&lt;br /&gt;
CS11&amp;lt;br /&amp;gt;&lt;br /&gt;
CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein&lt;br /&gt;
Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet&lt;br /&gt;
ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&amp;lt;br /&amp;gt;&lt;br /&gt;
TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet,&lt;br /&gt;
d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0&lt;br /&gt;
erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register&lt;br /&gt;
verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts&lt;br /&gt;
gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs&lt;br /&gt;
auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem&lt;br /&gt;
Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst&lt;br /&gt;
danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle&lt;br /&gt;
Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register&lt;br /&gt;
und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau&lt;br /&gt;
die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&amp;lt;br /&amp;gt;&lt;br /&gt;
OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen&lt;br /&gt;
Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte&lt;br /&gt;
überein so wird ein sogenannter Output Compare Match ausgelöst. Die&lt;br /&gt;
entsprechenden Aktionen werden über die Timer/Counter 1 Control und&lt;br /&gt;
Status Register eingestellt..&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register&lt;br /&gt;
verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts&lt;br /&gt;
gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs&lt;br /&gt;
auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem&lt;br /&gt;
Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst&lt;br /&gt;
danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle&lt;br /&gt;
Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register&lt;br /&gt;
und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau&lt;br /&gt;
die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&amp;lt;br /&amp;gt;&lt;br /&gt;
ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es&lt;br /&gt;
kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäss Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
definierte Flanke erkannt wird so wird der aktuelle Inhalt des&lt;br /&gt;
Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register&lt;br /&gt;
kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag&lt;br /&gt;
Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register&lt;br /&gt;
alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des&lt;br /&gt;
Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das&lt;br /&gt;
Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal&lt;br /&gt;
am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das&lt;br /&gt;
Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler&lt;br /&gt;
betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach&lt;br /&gt;
wieder zurück auf 0.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und&lt;br /&gt;
zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
gespeicherten Wert erreicht wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw.&lt;br /&gt;
gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik&lt;br /&gt;
zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;)&lt;br /&gt;
ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert&lt;br /&gt;
verglichen wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein&lt;br /&gt;
Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers&lt;br /&gt;
eine Signalquelle angeschlossen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1&lt;br /&gt;
(steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu&lt;br /&gt;
diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt&lt;br /&gt;
werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die&lt;br /&gt;
Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der&lt;br /&gt;
konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann,&lt;br /&gt;
wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein&lt;br /&gt;
Vergleichswert definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes&lt;br /&gt;
ein Compare Match Interrupt ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt&lt;br /&gt;
ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP)&lt;br /&gt;
auftritt. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein,&lt;br /&gt;
wenn auch ein entsprechender Interrupt ausgelöst werden soll.&amp;lt;font face=&amp;quot;Helvetica&amp;quot; size=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung&lt;br /&gt;
von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters&lt;br /&gt;
von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
übereinstimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist,&lt;br /&gt;
welches anzeigt, dass der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 in das Input Capture Register ICR1 übertragen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;unsigned char x;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;x = 10;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;while (x &amp;gt;= 0) {&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; x--;&amp;lt;br /&amp;gt;&lt;br /&gt;
}&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe&lt;br /&gt;
niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
deklarierte Variable niemals kleiner als Null werden kann.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife&lt;br /&gt;
drehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern&lt;br /&gt;
erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog&lt;br /&gt;
aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter&lt;br /&gt;
von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im&lt;br /&gt;
Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig&lt;br /&gt;
wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb&lt;br /&gt;
unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein&lt;br /&gt;
spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen&lt;br /&gt;
zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht&lt;br /&gt;
mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen&lt;br /&gt;
das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht&lt;br /&gt;
wird, andernfalls wird der Watchdog nicht ausgeschaltet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4&lt;br /&gt;
Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1&lt;br /&gt;
steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&amp;lt;br /&amp;gt;&lt;br /&gt;
WDP1&amp;lt;br /&amp;gt;&lt;br /&gt;
WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog,&lt;br /&gt;
also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei&lt;br /&gt;
wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: (TODO link to avr-libc/modules/watchdog)&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Interrupt&lt;br /&gt;
ausgelöst wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je&lt;br /&gt;
nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang&lt;br /&gt;
geschaltet ist. Auf diese Weise bietet sich die Möglichkeit,&lt;br /&gt;
Software-Interrupts zu realisieren.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Interrupt&lt;br /&gt;
ausgelöst wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je&lt;br /&gt;
nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang&lt;br /&gt;
geschaltet ist. Auf diese Weise bietet sich die Möglichkeit,&lt;br /&gt;
Software-Interrupts zu realisieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine&lt;br /&gt;
Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird.&lt;br /&gt;
Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine&lt;br /&gt;
beendet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039;&lt;br /&gt;
eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine&lt;br /&gt;
Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird.&lt;br /&gt;
Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine&lt;br /&gt;
beendet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039;&lt;br /&gt;
eingeschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl&lt;br /&gt;
in den Schlafzustand versetzen zu können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Um den Schlafmodus nicht irrtümlich einzuschalten wird empfohlen,&lt;br /&gt;
das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu&lt;br /&gt;
setzen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit bestimmt der Schlafmodus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ist das Bit gelöscht so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ist das Bit gesetzt so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ISC11&amp;lt;br /&amp;gt;&lt;br /&gt;
ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;&lt;br /&gt;
Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese beiden Bits bestimmen, ob die steigende oder die fallende&lt;br /&gt;
Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet&lt;br /&gt;
wird.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert,&lt;br /&gt;
solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen&lt;br /&gt;
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&lt;br /&gt;
einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ISC01&amp;lt;br /&amp;gt;&lt;br /&gt;
ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;&lt;br /&gt;
Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese beiden Bits bestimmen, ob die steigende oder die fallende&lt;br /&gt;
Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet&lt;br /&gt;
wird.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert,&lt;br /&gt;
solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen&lt;br /&gt;
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&lt;br /&gt;
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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich können alle Interruptspezifischen Registerzugriffe wie&lt;br /&gt;
gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es&lt;br /&gt;
jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei&lt;br /&gt;
interrupt.h einbinden mittels&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und dann kann&#039;s losgehen&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind braucht es selbstverständlich noch&lt;br /&gt;
den auszuführenden Code, der ablaufen soll wenn ein Interrupt eintrifft.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu gibt es zwei Definitionen welche allerdings AVR-GCC spezifisch sind und&lt;br /&gt;
bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit SIGNAL wird eine Funktion für die Bearbeitung eines Interrupts&lt;br /&gt;
eingeleitet. Als Argument muss dabei die Benennung des entsprechenden&lt;br /&gt;
Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien&lt;br /&gt;
IOxxxx.h zu finden. Mögliche Funktionsrümpfe für solche Interruptfunktionen&lt;br /&gt;
sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des Funktion sind alle weiteren Interrupts&lt;br /&gt;
automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder&lt;br /&gt;
zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt&lt;br /&gt;
(gleiche oder andere Interruptquelle) auftreten so wird das entsprechende Bit im&lt;br /&gt;
zugeordneten Interrupt Flag Register gesetzt und die entsprechende&lt;br /&gt;
Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der&lt;br /&gt;
aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die&lt;br /&gt;
entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen&lt;br /&gt;
wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch&lt;br /&gt;
öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass&lt;br /&gt;
Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden&lt;br /&gt;
sollten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied&lt;br /&gt;
ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global&lt;br /&gt;
Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts&lt;br /&gt;
zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten&lt;br /&gt;
Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und&lt;br /&gt;
sollte wirklich nur dann angewendet werden wenn man sich absolut sicher ist, das&lt;br /&gt;
Ganze auch im Griff zu haben.&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Datenblock lesen ===&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele, structs, strings, union-&amp;quot;trick&amp;quot; fuer floats&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Variablen&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01ff), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich noch den Ablageort (Flash oder RAM) sichern. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer Präprozessor-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: read_block erläutern &amp;quot;union-trick&amp;quot;, structs etc.&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
...&lt;br /&gt;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem [[WinAVR]]-Beispielmakefile ersichtlich.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Externe Baugrupen =&lt;br /&gt;
==I2C==&lt;br /&gt;
===Master===&lt;br /&gt;
[http://homepage.sunrise.ch/mysunrise/pfleury/group__pfleury__ic2master.html I2C Master library]&lt;br /&gt;
===Slave===&lt;br /&gt;
==CAN==&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== HD44870===&lt;br /&gt;
[http://homepage.sunrise.ch/mysunrise/pfleury/group__pfleury__lcd.html LCD]&lt;br /&gt;
=== Nokia3310===&lt;br /&gt;
=== Nokia 6100 LCD===&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial&amp;diff=4343</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=4343"/>
		<updated>2004-09-30T10:47:37Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: quand = quad&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Programmierung der AVR-Mikrocontroller mit C =&lt;br /&gt;
&lt;br /&gt;
Dieses Tutorial soll dem Einsteiger helfen, mit der Programmiersprache &#039;&#039;&#039;C&#039;&#039;&#039;&lt;br /&gt;
die Mikrocontroller der Atmel AVR-Reihe zu programmieren.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es gibt einige Gründe, weshalb eine höhere Programmiersprache der reinen&lt;br /&gt;
Assembler-Programmierung vorgezogen werden kann. So kann mit &#039;&#039;&#039;C&#039;&#039;&#039; das&lt;br /&gt;
Programm wesentlich lesbarer geschrieben werden als in Assembler.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es soll allerdings auch nicht verheimlicht werden, dass in Assembler in der&lt;br /&gt;
Regel schnellerer Programmcode entwickelt werden kann als mit &#039;&#039;&#039;C&#039;&#039;&#039;. Meiner&lt;br /&gt;
Meinung nach jedoch können wir 99 Prozent aller Aufgaben problemlos mit &#039;&#039;&#039;C&#039;&#039;&#039;&lt;br /&gt;
meistern und für die ganz krassen Fälle kann sogar Assembler-Code direkt in&lt;br /&gt;
ein &#039;&#039;&#039;C&#039;&#039;&#039;-Programm eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Der Autor hat sich alle Mühe gegeben, sein Wissen hier fehlerfrei wiederzugeben. Fehler können jedoch nicht ausgeschlossen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Fehlermeldungen bitte an christian.schifferle@bluewin.ch&lt;br /&gt;
&lt;br /&gt;
Der Autor übernimmt keinerlei Haftung für etwaige Schäden wie&lt;br /&gt;
z.B. durchgeknallte Sicherungen,&lt;br /&gt;
welche durch die Verwendung dieses Dokuments entstehen könnten.&lt;br /&gt;
&lt;br /&gt;
Die deutschen Leser mögen mir verzeihen, dass ich, wie es in&lt;br /&gt;
der Schweiz üblich ist, kein scharfes &amp;amp;szlig; verwendet habe.&lt;br /&gt;
&lt;br /&gt;
Die vorliegende Dokumentation ist und bleibt geistiges Machwerk&lt;br /&gt;
von Christian Schifferle und darf nicht als Eigenproduktion angepriesen werden.&lt;br /&gt;
&lt;br /&gt;
Das Dokument darf nach Belieben an Dritte weiter gegeben werden,&lt;br /&gt;
sofern der Copyright-Hinweis auf den Autor nicht verändert oder gelöscht wird.&lt;br /&gt;
&lt;br /&gt;
Der Autor, nämlich ich, das ist:&lt;br /&gt;
&lt;br /&gt;
Christian Schifferle&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Risweg 7&amp;lt;br /&amp;gt;&lt;br /&gt;
CH-4624 Härkingen&amp;lt;br /&amp;gt;&lt;br /&gt;
E-Mail: [mailto:christian.schifferle@bluewin.ch christian.schifferle@bluewin.ch]&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | &amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Achtung:&amp;lt;/font&amp;gt;&lt;br /&gt;
| style=&amp;quot;border-style: solid; border-color: #FF0000&amp;quot; bgcolor=&amp;quot;#FFFF00&amp;quot; | Der gesamte Kurs als ZIP-Datei kann&lt;br /&gt;
[http://www.mypage.bluewin.ch/ch_schifferle/Atmel.zip hier]&lt;br /&gt;
&lt;br /&gt;
herunter geladen werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die ZIP-Datei muss dann auf dem eigenen Rechner in ein beliebiges&lt;br /&gt;
Verzeichnis entpackt werden. Die Startdatei heisst dann Index.htm.&lt;br /&gt;
&lt;br /&gt;
Für diejenigen, welche neu in die Programmiersprache C einsteigen,&lt;br /&gt;
empfiehlt es sich, zuvor die ebenfalls [http://www.mypage.bluewin.ch/ch_schifferle/C-Kurs.zip hier]&lt;br /&gt;
erhältliche Einführung in die Programmiersprache C durchzuarbeiten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Aktualisierung =&lt;br /&gt;
&lt;br /&gt;
Viele der im Original-Dokument von C. Schifferle verwendeten Funktionen sind in &lt;br /&gt;
aktuellen Versionen des avr-gcc C-Compilers und der avr-libc zwar noch enthalten, &lt;br /&gt;
aber als deprecated ausgewiesen. D.h. diese Funktionen sollten nicht mehr verwendet &lt;br /&gt;
werden und existieren in zukünftigen Versionen des Compilers/der Library nicht mehr&lt;br /&gt;
(z.B. sbi(), cbi(), outp(), inp()). Der Artikel wurde auf die neuen Funktionen/Methoden &lt;br /&gt;
angepasst und stimmt somit nicht mehr mit den Informationen im zip-Achiv überein.&lt;br /&gt;
Einige Textpassagen entsprechen nicht mehr dem Originaldokument, einige Codebeispiele wurden hinzugefügt oder erweitert, der Abschnitt &amp;quot;Speicher&amp;quot; ist neu.&lt;br /&gt;
&lt;br /&gt;
In diesem Text wird häufig auf die Standardbibliothek für den avr-gcc-Compiler, die avr-libc, verwiesen. Die &#039;&#039;&#039;Dokumentation der avr-libc&#039;&#039;&#039; findet sich &lt;br /&gt;
[http://www.nongnu.org/avr-libc/user-manual/index.html hier]. Bei WinAVR gehört diese Dokumentation zum Lieferumfang und wird mitinstalliert. &lt;br /&gt;
&lt;br /&gt;
(M. Thomas Sept./04)&lt;br /&gt;
&lt;br /&gt;
= Benötigte Werkzeuge =&lt;br /&gt;
&lt;br /&gt;
Um die Übungen in diesem Tutorial nachvollziehen zu können benötigen wir folgende Hard- und Software:&lt;br /&gt;
&lt;br /&gt;
* Testboard für die Aufnahme eines AVR Controllers Ihrer Wahl. Dieses Testboard kann durchaus auch selber zusammen gelötet werden. So arbeite ich mit einem Testboard, welches ich mir auf einer Veroboard-Platine zusammen gestellt habe. Für die Versuche bzw. Übungen in diesem Tutorial habe ich jeweils einen AT90S2313 verwendet. &amp;lt;!-- veraltet: Dieser Controller weist meiner Meinung nach das beste Preis-/Leistungsverhältnis auf. --&amp;gt;&lt;br /&gt;
* AVRGCC-Compiler&amp;lt;br /&amp;gt; Ich beschränke mich hier auf den GCC Compiler, weil ich diesen selber verwende und weil er kostenlos zu haben und weit verbreitet ist. Siehe auch: [[AVR-GCC]]&lt;br /&gt;
* Programmiersoftware und Hardware z.B. PonyProg (siehe auch: [[Pony-Prog Tutorial]]) oder [[AVRDUDE]] mit [[STK200]]-Dongle oder die von Atmel verfügbare Hard- und Software ([[STK500]], [[AVR-Studio]]). &lt;br /&gt;
&amp;lt;!--, und natürlich ein passendes Kabel, um die Programme auf den Atmel übertragen zu können.--&amp;gt;&lt;br /&gt;
* Wenn man debuggen will eventuell [[AVR-Studio]]&lt;br /&gt;
&lt;br /&gt;
= Was tun, wenn&#039;s nicht &amp;quot;klappt&amp;quot;? =&lt;br /&gt;
&lt;br /&gt;
* Herausfinden, ob es tatsächlich ein avr(-gcc) spezifisches Problem ist oder die 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;
* Die [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] lesen, vor allem (aber nicht nur) den Abschnitt Related Pages/Frequently Asked Questions = Oft gestellte Fragen (und Antworten dazu). Z.Zt leider nur in englischer Sprache verfügbar.&lt;br /&gt;
* Den Artikel [[AVR-GCC]] in diesem Wiki lesen.&lt;br /&gt;
* Das gcc-Forum auf [http://www.mikrocontroller.net www.mikrocontroller.net] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das avr-gcc-Forum bei [http://www.avrfreaks.net avrfreaks] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Das [http://www.avr1.org/pipermail/avr-gcc-list/ Archiv der avr-gcc Mailing-Liste] nach vergleichbaren Problemen absuchen.&lt;br /&gt;
* Nach Beispielcode suchen. Vor allem in der Academy von [http://www.avrfreaks.net AVRFREAKS] (anmelden).&lt;br /&gt;
* Google oder alltheweb befragen schadet nie.&lt;br /&gt;
* Bei Problemen mit der Ansteuerung interner AVR-Funktionen mit C-Code: das Datenblatt des Controllers lesen (ganz und am Besten zweimal)&lt;br /&gt;
* einen Beitrag in eines der Foren oder eine Mail an die Mailing-Liste schreiben. Dabei mgl. viel Information geben: Controller, Compilerversion, genutzte Bibliotheken, Ausschnitte aus dem Quellcode, 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: [http://www.lugbz.org/documents/smart-questions_de.html &amp;quot;Wie man Fragen stellt&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
= Exkurs: makefiles =&lt;br /&gt;
&lt;br /&gt;
Wenn man bisher gewohnt ist, mit integrierten Entwicklungsumgebung à la Visual-C Programme zu erstellen, wirkt das makefile-Konzept auf den ersten Blick etwas kryptisch. Nach kurzer Einarbeitung ist diese Vorgehensweise jedoch sehr praktisch. Diese Dateien (üblicher Name: &#039;makefile&#039; ohne Dateiendung) dienen der Ablaufsteuerung des Programms make, das auf allen Unix/Linux-Systemen installiert sein sollte, und in einer Fassung fuer MS-Windows auch in [[WinAVR]] (Unterverzeichnis utils/bin) enthalten ist.&lt;br /&gt;
&lt;br /&gt;
Im Unterverzeichnis &#039;&#039;sample&#039;&#039; einer WinAVR-Installation findet man eine sehr brauchbare Vorlage, die sich einfach an das eigene Projekt anpassen lässt ([[Media:Makefile|lokale Kopie Stand Sept. 2004]]). Wahlweise kann man auch [http://www.sax.de/~joerg/mfile/ mfile] von Jörg Wunsch nutzen. mfile erzeugt ein makefile nach Einstellungen in einer grafischen Nutzeroberfläche, wird bei WinAVR mitinstalliert, ist aber als TCL/TK-Programm auf nahezu allen Platformen lauffähig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Die folgenden Ausführungen beziehen sich auf das WinAVR Beispiel-Makefile.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile alles richtig eingestellt genügt es, sich drei Parameter zu merken, die über die shell bzw. die Windows-Kommandozeile (cmd.exe/command.exe) als Parameter an &amp;quot;make&amp;quot; übergeben werden. Das Programm make sucht sich &amp;quot;automatisch&amp;quot; das makefile im aktuellen Arbeitsverzeichnis und führt die darin definierten Operationen für den entsprechenden Aufrufparameter durch.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| &#039;&#039;make all&#039;&#039;&lt;br /&gt;
| Erstellt aus den in im makefile angegebenen Quellcodes eine &#039;&#039;hex&#039;&#039;-Datei (und ggf. auch &#039;&#039;eep&#039;&#039;-Datei).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make program&#039;&#039;&lt;br /&gt;
| Überträgt die hex-Datei (und wahlweise auch die eep-Datei für den EEPROM) zum AVR. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;make clean&#039;&#039;&lt;br /&gt;
| löscht alle temporären Dateien, also auch die hex-Datei&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Aufrufe können in die allermeisten Editoren in &amp;quot;Tool-Menüs&amp;quot; eingebunden werden. Dies erspart den Kontakt mit der Kommandozeile. (Bei WinAVR sind die Einträge schon im Tools-Menü des mitgelieferten Editors Programmers-Notepad eingetragen.)&lt;br /&gt;
&lt;br /&gt;
Üblicherweise sind folgende Daten im makefile anzupassen:&lt;br /&gt;
* Controllertyp&lt;br /&gt;
* Quellcode-Dateien (c-Dateien)&lt;br /&gt;
* Typ und Anschluss des Programmiergeräts&lt;br /&gt;
&lt;br /&gt;
Die in den folgenden Unterabschnitten gezeigten makefile-Auschnitte sind für ein Programm, das auf einem ATmega8 ausgeführt werden soll. Der Quellcode besteht aus den c-Dateien superprog.c (darin main()), uart.c, lcd.c und 1wire.c. Im Quellcodeverzeichnis befinden sich diese Dateien: superprog.c, uart.h, uart.c, lcd.h, lcd.c, 1wire.h, 1wire.c und das makefile (die angepasste Kopie des WinAVR-Beispiels).&lt;br /&gt;
&lt;br /&gt;
Der Controller wird mittels [[avrdude]] über ein [[STK200]]-Programmierdongle an der Schnittstelle lpt1 (bzw. /dev/lp0) programmiert. Im Quellcode sind auch Daten für die &#039;&#039;section .eeprom&#039;&#039; definiert (siehe Abschnitt Speicherzugriffe TODO: nach unten verlinken), diese sollen beim Programmieren gleich mit ins EEPROM geschrieben werden. &lt;br /&gt;
&lt;br /&gt;
== Controllertyp setzen ==&lt;br /&gt;
&lt;br /&gt;
Dazu wird die &amp;quot;make-Variable&amp;quot; MCU ensprechend dem Namen des verwendenten Controllers gesetzt. Ein Liste der von avr-gcc und der avr-libc untersützten Typen findet sich in der [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
# Kommentare in Makefiles beginnen mit einem Doppelkreuz &lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# ATmega8 at work&lt;br /&gt;
MCU = atmega8&lt;br /&gt;
# oder MCU = atmega16 &lt;br /&gt;
# oder MCU = at90s8535&lt;br /&gt;
# oder ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quellcode-Dateien einstellen ==&lt;br /&gt;
&lt;br /&gt;
Den Namen der Quellcodedatei welche die Funktion main enthält, wird hinter TARGET eingetragen. Dies jedoch ohne die Endung &#039;&#039;.c&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
TARGET = superprog&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besteht das Projekt wie im Beispiel aus mehr als einer Quellcodedatei, sind die weiteren c-Dateien (nicht die Header-Dateien) durch Leerzeichen getrennt bei SRC einzutragen. Die bei TARGET definierte Datei ist schon eingetragen. Diesen Eintrag nicht löschen!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
SRC = $(TARGET).c uart.c lcd.c 1wire.c &lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man die Liste der Quellcodedateien auch mit dem Operator += erweitern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SRC = $(TARGET).c uart.c 1wire.c&lt;br /&gt;
# lcd-Code fuer Controller xyz123 (auskommentiert)&lt;br /&gt;
# SRC += lcd_xyz.c&lt;br /&gt;
# lcd-Code fuer &amp;quot;Standard-Controller&amp;quot; (genutzt)&lt;br /&gt;
SRC += lcd.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programmiergerät einstellen ==&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen sind auf die Programmiersoftware avrdude angepasst, jedoch lässt sich auch andere Programmiersoftware einbinden, sofern diese über Kommandozeile gesteuert werden kann (z.B. stk500.exe, uisp, sp12).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
# Einstellung fuer STK500 an com1 (auskommentiert)&lt;br /&gt;
# AVRDUDE_PROGRAMMER = stk500&lt;br /&gt;
# com1 = serial port. Use lpt1 to connect to parallel port.&lt;br /&gt;
# AVRDUDE_PORT = com1    # programmer connected to serial device&lt;br /&gt;
&lt;br /&gt;
# Einstellung fuer STK200-Dongle an lpt1&lt;br /&gt;
AVRDUDE_PROGRAMMER = stk200&lt;br /&gt;
AVRDUDE_PORT = lpt1&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Flash(=.hex) und EEPROM(=.eep) zusammen auf den Controller programmiert werden, ist das Kommentarzeichen vor AVRDUDE_WRITE_EEPROM zu löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
#Auskommentiert: EERPOM-Inhalt wird nicht mitgeschrieben&lt;br /&gt;
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
&lt;br /&gt;
#Nich-auskommentiert EERPOM-Inhalt wird mitgeschrieben&lt;br /&gt;
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Die Eingabe von &#039;&#039;make all&#039;&#039; im Arbeitsverzeichnis mit dem makefile und den Quellcodedateien erzeugt (unter anderem) die Dateien superprog.hex und superprog.eep. Abhängigkeiten zwischen den einzelnen c-Dateien werden dabei automatisch berücksichtigt. Die &#039;&#039;superprog.hex&#039;&#039; und &#039;&#039;superprog.eep&#039;&#039; werden mit &#039;&#039;make program&#039;&#039; zum Controller  übertragen. Mit &#039;&#039;make clean&#039;&#039; werden alle temporären Dateien gelöscht (=&amp;quot;aufgeräumt&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
== Eingabedateien zur Simulation in AVR-Studio ==&lt;br /&gt;
&lt;br /&gt;
Mit älteren AVR-Studio-Versionen kann man nur auf Grundlage sogenannter &#039;&#039;coff&#039;&#039;-Dateien simulieren. Neuere Versionen von AVR-Studio (ab 4.10.???) unterstützen zudem das modernere aber noch experimentelle dwarf-2-Format, das ab WinAVR 20040722 (avr-gcc 3.4.1/Binutils inkl. Atmel add-ons) &amp;quot;direkt&amp;quot; vom Compiler erzeugt wird.&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei extcoff:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=stabs&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make extcoff&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;cof&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.cof&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
; Vorgehensweise bei dwarf-2:&lt;br /&gt;
* im Makefile bei DEBUG: &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;DEBUG=dwarf-2&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &#039;&#039;make all&#039;&#039; (evtl. vorher &#039;&#039;make clean&#039;&#039;)&lt;br /&gt;
* die erzeugte &#039;&#039;elf&#039;&#039;-Datei (im Beispiel oben &#039;&#039;superprog.elf&#039;&#039;) in AVR-Studio laden&lt;br /&gt;
* AVR-Simulator und zu simulierenden Controller wählen, &amp;quot;Finish&amp;quot;&lt;br /&gt;
* weiteres siehe AVR-Studio Online-Hilfe&lt;br /&gt;
&lt;br /&gt;
Beim Simulieren scheinen oft &amp;quot;Variablen zu fehlen&amp;quot;. Ursache dafür ist, dass der Compiler die &amp;quot;Variablen&amp;quot; direkt Registern zuweist. Dies kann vermieden werden, indem die Optimierung abgeschaltet wird (im makefile). Man simuliert dann jedoch ein vom optimierten Code stark abweichendes Programm. Das Abschalten der Optimierung wird nicht empfohlen.&lt;br /&gt;
&lt;br /&gt;
= Definition einiger Datentypen =&lt;br /&gt;
&lt;br /&gt;
Für die Programmierung von Mikrocontrollern ist es sinnvoll, dass wir uns&lt;br /&gt;
vorerst einige Datentypen definieren, welche den Zugriff auf die verschiedenen&lt;br /&gt;
Komponenten des Controllers vereinfachen oder wenigstens das Programm lesbarer&lt;br /&gt;
machen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef unsigned char BYTE;&lt;br /&gt;
typedef unsigned short WORD;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYTE ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp BYTE definiert eine Variable mit 8 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 255.&lt;br /&gt;
&lt;br /&gt;
== WORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp WORD definiert eine Variable mit 16 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 65535.&lt;br /&gt;
&lt;br /&gt;
== DWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp DWORD (gesprochen: Double-Word) definiert eine Variable mit 32 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 4294967295.&lt;br /&gt;
&lt;br /&gt;
== QWORD ==&lt;br /&gt;
&lt;br /&gt;
Der Datentyp QWORD (gesprochen: Quad-Word) definiert eine Variable mit 64 Bit Breite zur Darstellung&lt;br /&gt;
von ganzen Zahlen im Bereich zwischen 0 ... 1.84467440737E19.&lt;br /&gt;
&lt;br /&gt;
= Standardisierte Typen =&lt;br /&gt;
&lt;br /&gt;
Standardisierte Datentypen werden in der Header-Datei inttypes.h definiert.&lt;br /&gt;
Will man diese Typen benutzen, bindet man die &amp;quot;Definitionsdatei&amp;quot; wie folgt ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einige der dort definierte Typen (avr-libc Version 1.0.4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
typedef signed char int8_t;&lt;br /&gt;
typedef unsigned char uint8_t;&lt;br /&gt;
typedef short int16_t;&lt;br /&gt;
typedef unsigned short uint16_t;&lt;br /&gt;
typedef long int32_t;&lt;br /&gt;
typedef unsigned long uint32_t;&lt;br /&gt;
typedef long long int64_t;&lt;br /&gt;
typedef unsigned long long uint64_t;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Vierfach-Wort (64Bit) entspricht beim AVR uint64_t, ein Doppel-Wort (32bit) uint32_t, ein Wort (16bit) uint16_t und ein Byte (8bit) uint8_t. &lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Integer Types&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef signed char int8_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
typedef unsigned char uint8_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef int int16_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
typedef unsigned int uint16_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef long int32_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
typedef unsigned long uint32_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef long long int64_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
typedef unsigned long long uint64_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;typedef int16_t intptr_t;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef uint16_t uintptr_t;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== &#039;&#039;&#039;Bitfelder&#039;&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren von Mikrocontrollern muss auf jedes Byte oder sogar auf&lt;br /&gt;
jedes Bit geachtet werden. Oft müssen wir in einer Variablen lediglich den&lt;br /&gt;
Zustand 0 oder 1 speichern. Wenn wir nun zur Speicherung eines einzelnen Wertes&lt;br /&gt;
den kleinsten bekannten Datentypen, nämlich &#039;&#039;&#039;unsigned char&#039;&#039;&#039;, nehmen, dann&lt;br /&gt;
verschwenden wir 7 Bits, da ein &#039;&#039;&#039;unsigned char&#039;&#039;&#039; ja 8 Bit breit ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier bietet uns die Programmiersprache C ein mächtiges Werkzeug an, mit dessen&lt;br /&gt;
Hilfe wir 8 Bits in einer einzelnen Bytevariable zusammen fassen und (fast) wie&lt;br /&gt;
8 einzelne Variablen ansprechen können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Rede ist von sogenannten Bitfeldern. Diese werden als Strukturelemente&lt;br /&gt;
definiert. Sehen wir uns dazu doch am besten gleich ein Beispiel an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
struct {&lt;br /&gt;
   unsigned char bStatus_1:1; // 1 Bit für bStatus_1&lt;br /&gt;
   unsigned char bStatus_2:1; // 1 Bit für bStatus_2&lt;br /&gt;
   unsigned char bNochNBit:1; // Und hier noch mal ein Bit&lt;br /&gt;
   unsigned char b2Bits:2;    // Dieses Feld ist 2 Bits breit&lt;br /&gt;
   // All das hat in einer einzigen Byte-Variable Platz.&lt;br /&gt;
   // die 3 verbleibenden Bits bleiben ungenutzt&lt;br /&gt;
} x;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zugriff auf ein solches Feld erfolgt nun wie beim Strukturzugriff bekannt&lt;br /&gt;
über den Punkt- oder den Dereferenzierungs-Operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x.bStatus_1 = 1;&lt;br /&gt;
x.bStatus_2 = 0;&lt;br /&gt;
x.b2Bits = 3;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(mt Empfehlung: Bitfelder sparen zwar Platz, verschlechtern aber unter&lt;br /&gt;
Umständen die Les- und Wartbarkeit des Codes. Anfängern wird geraten,&lt;br /&gt;
ein &amp;quot;ganzes&amp;quot; ein Byte (uint8_t) zu nutzen auch wenn nur ein Bitwert gespeichert &lt;br /&gt;
werden soll.)&lt;br /&gt;
&lt;br /&gt;
= Grundsätzlicher Programmaufbau eines &amp;amp;micro;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;
Bei dieser Programmiertechnik wird eine Endlosschleife programmiert, welche im&lt;br /&gt;
Wesentlichen immer den gleichen Aufbau hat:&lt;br /&gt;
&lt;br /&gt;
[[Image:Sequentielle Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
== Interruptgesteuerter Programmablauf ==&lt;br /&gt;
&lt;br /&gt;
Bei dieser Methode werden beim Programmstart zuerst die gewünschten&lt;br /&gt;
Interruptquellen aktiviert und dann in eine Endlosschleife gegangen, in welcher&lt;br /&gt;
Dinge erledigt werden können, welche nicht zeitkritisch sind.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn ein Interrupt ausgelöst wird so wird automatisch die zugeordnete&lt;br /&gt;
Interruptfunktion ausgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
= Allgemeiner 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 heisst, das Programm kann die&lt;br /&gt;
Inhalte der Register auslesen und beschreiben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Einige Register haben spezielle Funktionen, andere wiederum könne für&lt;br /&gt;
allgemeine Zwecke (Speichern von Datenwerten) verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Einzelne Register sind bei allen AVR&#039;s vorhanden, andere wiederum nur bei&lt;br /&gt;
bestimmten Typen. So sind beispielsweise die Register, welche für den Zugriff&lt;br /&gt;
auf den UART notwendig sind selbstverständlich nur bei denjenigen Modellen&lt;br /&gt;
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&lt;br /&gt;
AVR-Typen definiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;!--Wenn im Makefile der MCU-Typ definiert ist so bindet das System automatisch die&lt;br /&gt;
richtige Headerdatei ein.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Wenn im Makefile der MCU-Typ definiert ist, wird vom System automatisch die&lt;br /&gt;
zum Typen passende Definitionsdatei genutzt, sobald man im Code die allgemeine &lt;br /&gt;
&amp;quot;io.h&amp;quot; Header-Datei einbinden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im Makefile der MCU Type z.B. mit dem Inhalt atmega8 definiert,&lt;br /&gt;
wird beim einlesen der io.h-Datei implizit (&amp;quot;automatisch&amp;quot;) auch die iom8.h-Datei mit&lt;br /&gt;
den Register-Definitionen für den ATmega8 eingelesen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Man kann der MCU-Typ aber selbstverständlich auch noch in der C-Quelldatei&lt;br /&gt;
definieren, wenn man Freude daran hat. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== I/O-Register ==&lt;br /&gt;
&lt;br /&gt;
Die I/O-Register haben einen besonderen Stellenwert bei den AVR Controllern.&lt;br /&gt;
Sie dienen dem Zugriff auf die Ports und die Schnittstellen des Controllers.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir unterscheiden zwischen 8-Bit und 16-Bit Registern. Vorerst behandeln wir mal&lt;br /&gt;
die 8-Bit Register.&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Hinweis:&#039;&#039;&#039;&lt;br /&gt;
| Die folgenden Funktionen erwarten als Argument&lt;br /&gt;
für das jeweilige Portregister konstante Werte. Am besten verwenden wir&lt;br /&gt;
die entsprechenden defines aus der Headerdatei . Wenn die&lt;br /&gt;
Portadresse über eine 8-Bit Variable übergeben quittiert der Inline&lt;br /&gt;
Assembler dies jeweils mit 2 Fehlermeldungen folgender Form:&lt;br /&gt;
&lt;br /&gt;
:: warning: asm operand 0 probably&lt;br /&gt;
doesn&#039;t match constraints&amp;lt;br /&amp;gt;:: warning: asm operand 1 probably&lt;br /&gt;
doesn&#039;t match constraints&lt;br /&gt;
&lt;br /&gt;
Offensichtlich läuft das Programm so auch tatsächlich nicht korrekt&lt;br /&gt;
ab. Wenn wir also Ports über Variablen ansprechen wollen müssen wir auf&lt;br /&gt;
die Low Level-Funktion &#039;&#039;&#039;[http://en.wikipedia.org#Speicherbezogener%20Portzugriff __mmio]&#039;&#039;&#039;&lt;br /&gt;
ausweichen.&lt;br /&gt;
|} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lesen eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Der gesamte Inhalt eines Registers kann einfach mit dem Befehl&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
ausgelesen werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Lesen kann man auf I/O Register einfach wie auf eine&lt;br /&gt;
Variable zugreifen. Die spezielle Funktion inp() ist nicht mehr &lt;br /&gt;
notwendig und veraltet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
uint8_t foo;&lt;br /&gt;
...&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  foo=PINB; /* kopiert den Status der Eingabepins an PortB in die Variable foo */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lesen eines Bits ====&lt;br /&gt;
&lt;br /&gt;
Die AVR-Bibliothek stellt auch Funktionen zur Abfrage eines einzelnen Bits&lt;br /&gt;
eines Registers zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
;bit_is_set (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_set&#039;&#039; prüft, ob ein Bit gesetzt ist. Wenn das Bit gesetzt ist wird ein Wert ungleich 0 zurückgegeben. Genau genommen ist es die Wertigkeit des abgefragten Bits, also 1 für Bit0, 2 für Bit1, 4 für Bit2 etc.&lt;br /&gt;
&lt;br /&gt;
;bit_is_clear (&amp;lt;Register&amp;gt;,&amp;lt;Bitnummer&amp;gt;) : Die Funktion &#039;&#039;bit_is_clear&#039;&#039; prüft, ob ein Bit gelöscht ist. Wenn das Bit gelöscht ist, also auf 0 ist, wird ein Wert ungleich 0 zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Die Funktionen bit_is_clear bzw. bit_is_set sind nicht unbedingt erfoderlich, man kann auch &amp;quot;einfachen&amp;quot; C-Syntax verwenden. Der universell verwendbar ist. &#039;&#039;bit_is_set&#039;&#039; entspricht dabei z.B. (Variable &amp;amp; (1&amp;lt;&amp;lt;Bitnummer)). Das Ergebnis ist &amp;lt;&amp;gt;0 wenn das Bit gesetzt und 0 wenn nicht gesetzt ist. &lt;br /&gt;
&lt;br /&gt;
* siehe auch [[Bitmanipulation]]&lt;br /&gt;
&lt;br /&gt;
=== Schreiben eines I/O-Registers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Zum Beschreiben eines I/O-Registers verwendet man allgemein den Befehl&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Als Wert muss die Bitmaske mit den Werten aller Pins angegeben werden. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schreiben kann man I/O Register einfach wie eine&lt;br /&gt;
Variable setzen. Die spezielle Funktion outp() ist nicht mehr &lt;br /&gt;
notwendig, veraltet und sollte nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
  DDRA=0xff; /* Setzt das Richtungsregister des Ports A auf 0xff (alle Pins als Ausgang) */&lt;br /&gt;
  PORTA=0x03; /* Setzt PortA auf 0x03, Bit 0 und 1 &amp;quot;high&amp;quot;, restliche &amp;quot;low&amp;quot; */&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Schreiben eines Bits ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Auch für das Setzen bzw. Löschen eines einzelnen Bits eines I/O-Registers&lt;br /&gt;
stellt die AVR-Bibliothek entsprechende Funktionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;sbi&#039;&#039;&#039; setzt ein beliebiges Bit eines Registers, das heisst,&lt;br /&gt;
es wird der logische Wert 1 in das Bit geschrieben. Die anderen Bits des Ports&lt;br /&gt;
werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;cbi (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;cbi&#039;&#039;&#039; löscht ein beliebiges Bit eines Registers, das&lt;br /&gt;
heisst, es wird der logische Wert 0 in das Bit geschrieben. Die anderen Bits des&lt;br /&gt;
Ports werden nicht verändert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzelne Bits setzt man &amp;quot;Standard-C-Konform&amp;quot; mittels logischen (bit-) Operationen.&lt;br /&gt;
&lt;br /&gt;
mit dem Ausduck |=(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit gesetzt, mit dem Ausdruck&lt;br /&gt;
&amp;amp;=~(1&amp;lt;&amp;lt;Bitnummer) wird ein Bit geloescht. Die Funktionen sbi und cbi sind&lt;br /&gt;
dazu nicht notwendig, veraltet und sollten nicht mehr genutzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&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;/pre&amp;gt;&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;
=== Warten auf einen bestimmten Zustand ===&lt;br /&gt;
&lt;br /&gt;
Es gibt in der Bibliothek sogar Funktionen, die Warten, bis ein bestimmter&lt;br /&gt;
Zustand auf einem Bit erreicht ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings normalerweise eine eher unschöne Programmiertechnik.&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&lt;br /&gt;
definierte Bit gesetzt ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gesetzt ist wird die Funktion sofort wieder verlassen. Das niederwertigste Bit hat die Bitnummer 0. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 2&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;
&amp;lt;/pre&amp;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&lt;br /&gt;
definierte Bit gelöscht ist. Wenn das Bit beim Aufruf der Funktion bereits&lt;br /&gt;
gelöscht ist wird die Funktion sofort wieder verlassen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das niederwertigste Bit hat die Bitnummer 0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;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 4&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;
// ungleich 0 ist &lt;br /&gt;
while ( WARTEPIN &amp;amp; (1&amp;lt;&amp;lt;WARTEBIT) ) ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Universeller und auch auf andere Platformen 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;
&amp;lt;!-- mt: noch notwendig? &lt;br /&gt;
=== Speicherbezogener Portzugriff ===&lt;br /&gt;
&lt;br /&gt;
In der Regel sind die weiter oben erwähnten Funktionen zu bevorzugen. Es&lt;br /&gt;
kann aber auch sein, dass wird mal eine Stufe tiefer einsteigen müssen. Dann&lt;br /&gt;
verwenden wir für den Portzugriff das Synonym &#039;&#039;&#039;__mmio&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio()&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion dient dem speicherbasierten Zugriff auf die Ports (Memory&lt;br /&gt;
Mapped I/O).&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktion kann sowohl zum Lesen als auch zum Schreiben eines Ports verwendet&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
Um ein einzelnes Bit in einem Port zu setzen bzw. zu löschen kann also ein&lt;br /&gt;
der folgenden Befehlszeilen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__mmio () = __mmio () | (1&lt;br /&gt;
&amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp; // Setzt ein Bit&amp;lt;br /&amp;gt;&lt;br /&gt;
__mmio () = __mmio () &amp;amp; ~(1 &amp;lt;&amp;lt; )&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
// Löscht ein Bit&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die anderen Bits des Ports bleiben unverändert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zugriff auf Ports =&lt;br /&gt;
&lt;br /&gt;
Alle Ports der AVR-Controller werden über Register gesteuert. Dazu sind&lt;br /&gt;
jedem Port 3 Register zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;DDRx&#039;&#039;&#039; &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. (anhä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;
| &#039;&#039;&#039;PORTx&#039;&#039;&#039;&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. Wird ein Port als Eingang geschaltet, so können mit diesem Register&lt;br /&gt;
die internen Pull-Up Widerstände aktiviert oder deaktiviert werden (1 = aktiv).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PINx&#039;&#039;&#039;&lt;br /&gt;
| Eingangadresse für Port&#039;&#039;&#039;x&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!-- TODO: Erläuterung eher weiter unten nicht in der &amp;quot;Kurzzusammenfassung - Dies ist kein eigentliches Register, sondern definiert lediglich eine Adresse, in welcher der aktuelle Zustand der Eingangspins eines Ports vom&lt;br /&gt;
Controller abgelegt werden. Nichtsdestotrotz erfolgt der Zugriff auf den Zustand der Pins genau so, wie wenn &#039;&#039;&#039;PINx&#039;&#039;&#039; ein normales Register wäre. Die Adresse kann nur gelesen und nicht beschrieben werden.--&amp;gt;&lt;br /&gt;
Zustand des Ports. Die Bits in PINx entsprechen dem Zustand der Portspins. Bit gesetzt (1) wenn Pin &amp;quot;high/an&amp;quot;, Bit gelöscht (0) wenn Portpin &amp;quot;low/aus&amp;quot;.&lt;br /&gt;
|}&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;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;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;
Wollen wir also beispielsweise Pin 0 bis 4 von Port B als Ausgänge&lt;br /&gt;
definieren so schreiben wir folgende Zeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;gt;&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x1F, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&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;
&lt;br /&gt;
DDRB=0x1F; /* direkte Zuweisung - unuebersichtlich */&lt;br /&gt;
/* mehr Tipparbeit aber uebersichtlicher: */&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Pins 5 bis 7 werden (da 0) als Eingänge geschaltet.&lt;br /&gt;
&lt;br /&gt;
=== Ganze Ports ===&lt;br /&gt;
&lt;br /&gt;
Um einen ganzen Port als Ausgang zu definieren, kann der folgende Befehl&lt;br /&gt;
verwendet werden:&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0xFF, DDRB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
DDRB=0xff;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der Port B als Ganzes als Ausgang geschaltet.&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&lt;br /&gt;
bzw. auszugeben.&lt;br /&gt;
&lt;br /&gt;
== Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun einen als Ausgang definierten Pin auf Logisch 1 setzen. Dazu schreiben wir den entsprechenden Wert in das Portregister des entsprechenden Ports.&lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x04, PORTB);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB=0x04; /* besser PORTB=(1&amp;lt;&amp;lt;DDB2) */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird also der Ausgang an Pin 2 gesetzt (Beachte, dass die Bits immer &#039;&#039;von 0 an&#039;&#039;&lt;br /&gt;
gezählt werden, das niederwertigste Bit ist also Bit 0 und nicht etwa Bit 1).&lt;br /&gt;
&lt;br /&gt;
Man beachte bitte, dass bei der Zuweisung mittels &#039;&#039;&#039;=&#039;&#039;&#039; &amp;lt;!-- Verwendung der &#039;&#039;&#039;outp&#039;&#039;&#039;-Funktion--&amp;gt; immer alle Pins gleichzeitig angegeben werden. Man sollte also zuerst den aktuellen Wert des Ports einlesen und das Bit des gewünschten Ports in diesen Wert einfliessen lassen. Will man also nur den dritten Pin (Bit Nr. 2) an PortB 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = PORTB | 0x04; /* besser: PORTB = PORTB | ( 1&amp;lt;&amp;lt;DDB2 ) */&lt;br /&gt;
/* vereinfacht durch Nutzung des |= Operators : */&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;DDB2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Es gibt jedoch in der AVRGCC-Bibliothek Funktionen, welche dies selbständig&lt;br /&gt;
machen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Funktionen lassen sich wie folgt verwenden:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;sbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 1 (EIN)&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
cbi (PORTB, 2);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Setzt Pin 2 auf Logisch 0 (AUS)&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
Die Funktionen cbi und sbi sind dazu nicht notwendig, veraltet und sollten &lt;br /&gt;
nicht mehr genutzt werden.&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 wird den Pegel über entsprechende Hardware (z.B. Optokoppler, Spannunsteiler &amp;quot;Levelshifter&amp;quot;) 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 unserer Controllers eine logische 1 (Vcc) oder eine logische 0 (Masse) anliegt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage der Zustände der Portpins erfolgt direkt über den Registernamen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;inp ()&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Wobei es hier sehr wichtig ist, zur Abfrage der Eingänge nicht etwa das Portregister &#039;&#039;&#039;PORTx&#039;&#039;&#039; zu verwenden, sondern die Porteingangsadresse &#039;&#039;&#039;PINx&#039;&#039;&#039;. Dies ist&lt;br /&gt;
ein oft gemachter Fehler!&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wollen wir also die aktuellen Signalzustände von Port D abfragen und in einer&lt;br /&gt;
Variable namens bPortD abspeichern so schreiben wir dazu folgende Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
bPortD = PIND;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;bPortD = inp (PIND);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasten und Schalter ===&lt;br /&gt;
&lt;br /&gt;
Der Anschluss mechanischer Kontakte an den Mikrocontroller gestaltet sich ebenfalls ganz einfach, wobei wir zwei unterschiedliche Methoden unterscheiden müssen (&#039;&#039;Active Low&#039;&#039; und &#039;&#039;Active High&#039;&#039;):&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Active Low&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Active High&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| [[Image:Active Low.gif]]&lt;br /&gt;
| [[Image:Active High.gif]]&lt;br /&gt;
|- &lt;br /&gt;
| Bei dieser Methode wird der Kontakt zwischen&lt;br /&gt;
den Eingangspin des Controllers und Masse geschaltet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Damit bei offenem Schalter der Controller kein undefiniertes Signal&lt;br /&gt;
bekommt wird zwischen die Versorgungsspannung und den Eingangspin ein&lt;br /&gt;
sogenannter Pull-Up Widerstand geschaltet. Dieser dient dazu, den Pegel&lt;br /&gt;
bei geöffnetem Schalter auf logisch 1 zu ziehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Widerstandswert des Pull-Up Widerstands ist an sich nicht kritisch. Es&lt;br /&gt;
muss jedoch beachtet werden, dass über den Widerstand ein Strom in den&lt;br /&gt;
Eingang fliesst, also sollte er nicht zu klein gewählt werden um den&lt;br /&gt;
Controller nicht zu zerstören. Wird er allerdings zu hoch gewählt ist&lt;br /&gt;
die Wirkung eventuell nicht gegeben. Als üblicher Wert haben sich 10&lt;br /&gt;
Kiloohm eingebürgert.&lt;br /&gt;
&lt;br /&gt;
Die AVR&#039;s haben sogar an den meisten Pins softwaremässig zuschaltbare&lt;br /&gt;
interne Pull-Up Widerstände, welche wir natürlich auch verwenden&lt;br /&gt;
können.&lt;br /&gt;
| Hier wird der Kontakt zwischen die&lt;br /&gt;
Versorgungsspannung und Masse geschaltet.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bei offener Schalterstellung kein undefiniertes Signal am Controller&lt;br /&gt;
ansteht wird zwischen den Eingangspin und die Masse ein Pull-Down&lt;br /&gt;
Widerstand geschaltet. Dieser dient dazu, den Pegel bei geöffneter&lt;br /&gt;
Schalterstellung auf logisch 0 zu halten,&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Pull-Up Widerstände aktivieren ====&lt;br /&gt;
&lt;br /&gt;
Die internen Pull-Up Widerstände von Vcc zu den einzelnen Portpins werden&lt;br /&gt;
über das Register &#039;&#039;&#039; PORTx&#039;&#039;&#039; aktiviert bzw. deaktiviert, wenn ein Pin als &#039;&#039;&#039; Eingang&#039;&#039;&#039;&lt;br /&gt;
geschaltet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der Wert des entsprechenden Portpins auf 1 gesetzt so ist&lt;br /&gt;
der Pull-Up Widerstand aktiviert. Bei einem Wert von 0 ist der Pull-Up&lt;br /&gt;
Widerstand nicht aktiv.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man sollte jeweils entweder den internen oder einen externen Pull-Up Widerstand&lt;br /&gt;
verwenden, aber nicht beide zusammen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (0x00, DDRD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Port D als&lt;br /&gt;
Eingang schalten&amp;lt;br /&amp;gt;&lt;br /&gt;
outp (0x00, PORTD);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Interne Pull-Up Widerstände aus&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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: /* interer Pull-Up an allen Port-Pins aktivieren */&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird der gesamte Port D als Eingang geschaltet und alle Pull-Up&lt;br /&gt;
Widerstände aktiviert.&lt;br /&gt;
&lt;br /&gt;
===== Tastenentprellung =====&lt;br /&gt;
&lt;br /&gt;
Nun haben alle mechanischen Kontakte, sei es von Schaltern, Tastern oder auch von&lt;br /&gt;
Relais, die unangenehme Eigenschaft zu prellen. Dies bedeutet, dass beim&lt;br /&gt;
Schliessen des Kontaktes derselbe nicht direkt Kontakt herstellt, sondern&lt;br /&gt;
mehrfach ein- und ausschaltet bis zum endgültigen Herstellen des Kontaktes.&amp;lt;br /&amp;gt;&lt;br /&gt;
Soll nun mit einem schnellen Mikrocontroller gezählt werden, wie oft ein&lt;br /&gt;
solcher Kontakt geschaltet wird, dann haben wir ein Problem, weil das Prellen&lt;br /&gt;
als mehrfache Impulse gezählt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Diesem Phänomen muss beim Schreiben des Programms unbedingt Rechnung getragen&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
== Analog ==&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
ausgeben oder einlesen. Dazu müssen wir Umwege gehen, doch dies wird in einem späteren&lt;br /&gt;
Kapitel behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16-Bit Portregister (ADC, ICR1, OCR1, TCNT1) ===&lt;br /&gt;
&lt;br /&gt;
Einige der Portregister in den AVR-Controllern sind 16 Bit breit, Man spricht&lt;br /&gt;
dann von einem &#039;&#039;&#039;Wort&#039;&#039;&#039;. &lt;br /&gt;
&amp;lt;!--Für den Zugriff auf diese Register stellt die&lt;br /&gt;
Funktionsbibliothek zwei spezielle Befehle zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__inw ();&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Liest den aktuellen Wert eines 16-Bit Portregisters ein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;__outw (, );&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Schreibt einen Wert in ein 16-Bit Portregister. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Datenblatt sind diese Register üblicherweise mit dem Suffix &amp;quot;L&amp;quot; (LSB) und &amp;quot;H&amp;quot; (MSB) 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 kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
&lt;br /&gt;
= Der UART, Teil 1 =&lt;br /&gt;
&lt;br /&gt;
Wir werden in zukünftigen Übungen in die Situation kommen, dass wir&lt;br /&gt;
Informationen vom Controller auswerten bzw. anzeigen müssen wobei es vielleicht&lt;br /&gt;
dann nicht mehr reicht, ein paar Leuchtdioden anzusteuern.&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit brauchen wir eine Verbindung vom AVR zu unserem PC, und dies am besten&lt;br /&gt;
über die serielle Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben einen vollduplexfähigen UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal&lt;br /&gt;
&#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) schon eingebaut.&lt;br /&gt;
Dies ist eine wirklich feine Sache, haben wir doch damit schon das nötige&lt;br /&gt;
Werkzeug, um mit anderen Geräten, welche über eine serielle Schnittstelle&lt;br /&gt;
verfügen (insbesondere mit unserem PC), zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Übrigens: Vollduplex heisst nichts anderes, als dass der Baustein gleichzeitig&lt;br /&gt;
senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega) verfügen über einen oder zwei U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterschiedet sich vom UART häuptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
Der UART wird über vier separate Register angesprochen. USARTs der ATMEGAs verfügen über mehr als zusätzliche Konfigurations/Datenregister. Das Datenblatt gibt darüber Auskunft. Die Folgende Tabelle gibt nur die Register für die (veralteten) UARTs wieder.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXCIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART RX Complete Interrupt&lt;br /&gt;
ausgelöst wenn ein Zeichen vom UART empfangen wurde. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXCIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART TX Complete Interrupt&lt;br /&gt;
ausgelöst wenn ein Zeichen vom UART gesendet wurde. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDRIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein UART Datenregister Leer&lt;br /&gt;
Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues&lt;br /&gt;
zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt&lt;br /&gt;
Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Nur wenn dieses Bit gesetzt ist arbeitet der Empfänger des UART&lt;br /&gt;
überhaupt. Wenn das Bit nicht gesetzt ist kann der entsprechende&lt;br /&gt;
Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Nur wenn dieses Bit gesetzt ist arbeitet der Sender des UART&lt;br /&gt;
überhaupt. Wenn das Bit nicht gesetzt ist kann der entsprechende&lt;br /&gt;
Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CHR9&#039;&#039;&#039;&lt;br /&gt;
| 9 Bit Characters&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist können 9 Bit lange Zeichen übertragen&lt;br /&gt;
und empfangen werden. Das 9. Bit kann bei Bedarf als zusätzliches&lt;br /&gt;
Stopbit oder als Paritätsbit verwendet werden. Man spricht dann von&lt;br /&gt;
einem 11-Bit Zeichenrahmen:&amp;lt;br /&amp;gt;&lt;br /&gt;
1 Startbit + 8 Datenbits + 1 Stopbit + 1 Paritätsbit = 11 Bits&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXB8&#039;&#039;&#039;&lt;br /&gt;
| Receive Data Bit 8&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses&lt;br /&gt;
Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
| Transmit Data Bit 8&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses&lt;br /&gt;
Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor&lt;br /&gt;
das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USR&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;amp;amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;RXC&#039;&#039;&#039;&lt;br /&gt;
| UART Receive Complete&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom&lt;br /&gt;
Empfangs-Schieberegister in das Empfangs-Datenregister transferiert&lt;br /&gt;
wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Zeichen muss nun schnellstmöglich aus dem Datenregister&lt;br /&gt;
ausgelesen werden. Falls dies nicht erfolgt bevor ein weiteres&lt;br /&gt;
Zeichen komplett empfangen wurde wird eine Überlauf-Fehlersituation&lt;br /&gt;
eintreffen. Mit dem Auslesen des Datenregisters wird das Bit&lt;br /&gt;
automatisch gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TXC&#039;&#039;&#039;&lt;br /&gt;
| UART Transmit Complete&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister&lt;br /&gt;
befindliche Zeichen vollständig ausgegeben wurde und kein weiteres&lt;br /&gt;
Zeichen im Sendedatenregister ansteht. Dies bedeutet also, wenn die&lt;br /&gt;
Kommunikation vollumfänglich abgeschlossen ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das&lt;br /&gt;
Programm nach dem Senden von Daten auf Empfang schalten muss. Im&lt;br /&gt;
Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit nur dann automatisch gelöscht, wenn der entsprechende&lt;br /&gt;
Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit&lt;br /&gt;
selber löschen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDRE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn ein Zeichen vom&lt;br /&gt;
Sendedatenregister in das Send-Schieberegister übernommen wurde und&lt;br /&gt;
der UART nun wieder bereit ist, ein neues Zeichen zum Senden&lt;br /&gt;
aufzunehmen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn ein Zeichen in das&lt;br /&gt;
Sendedatenregister geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;FE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn der UART einen&lt;br /&gt;
Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines&lt;br /&gt;
empfangenen Zeichens 0 ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen&lt;br /&gt;
Zeichens 1 ist.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;ver&#039;&#039;&#039;R&#039;&#039;&#039;un&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im&lt;br /&gt;
Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das&lt;br /&gt;
nachfolgende Zeichen komplett empfangen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das nachfolgende Zeichen wird verworfen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in&lt;br /&gt;
das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne&lt;br /&gt;
kommunizieren möchten. Der Wert, der in dieses Register geschrieben&lt;br /&gt;
werden muss, errechnet sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
UBRR = Taktfrequenz / (Baudrate * 16) - 1&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis zu 115200 Baud und höher möglich.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (LOW) und 5V (HIGH). Die&lt;br /&gt;
Schnittstellenspezifikation für RS232 definiert jedoch -3V ... -12V (LOW) und&lt;br /&gt;
+3 ... +12V (HIGH). Zudem muss der Signalaustausch zwischen AVR und&lt;br /&gt;
Partnergerät invertiert werden. Für die Anpassung der Pegel und das&lt;br /&gt;
Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste&lt;br /&gt;
davon ist wohl der MAX232. Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den&lt;br /&gt;
TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im&lt;br /&gt;
einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2&lt;br /&gt;
Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach&lt;br /&gt;
gewünschter Funkstionsweise die&lt;br /&gt;
benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da wir vorerst nur senden möchten und (noch) keine Interrupts auswerten wollen&lt;br /&gt;
gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter&lt;br /&gt;
Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1 &amp;lt;&amp;lt; TXEN), UCR);&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UCR |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir noch die Baudrate festlegen. Gemäss unserer Formel brauchen&lt;br /&gt;
wir dazu die Taktfrequenz des angeschlossenen Oszillators bzw. Quarz in die&lt;br /&gt;
Formel einzufügen und das Resultat der Berechnung in das Baudratenregister des&lt;br /&gt;
UART einzuschreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#define F_CPU 4000000; // Zum Beispiel 4Mhz-Quarz&lt;br /&gt;
#define UART_BAUD_RATE 9600 // Wir versuchen mal mit 9600 Baud&lt;br /&gt;
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;#define F_CPU 4000000&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;br /&gt;
// Zum Beispiel 4Mhz-Quarz&amp;lt;br /&amp;gt;&lt;br /&gt;
#define UART_BAUD_RATE 9600 // Wir versuchen mal mit 9600 Baud&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (F_CPU / (UART_BAUD_RATE * 16L) - 1, UBRR);&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;aten &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp (&#039;x&#039;, UDR);&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // Schreibt das Zeichen x auf die Schnittstelle&amp;lt;/font&amp;gt;&#039;&#039;&#039; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
UDR = &#039;x&#039;; // Schreibt das Zeichen x auf die Schnittstelle&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Schreiben einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun mehrere, aufeinanderfolgende Zeichen auf die Schnittstelle&lt;br /&gt;
ausgeben wollen, dann müssen wir mit dem nächsten Zeichen warten, bis das&lt;br /&gt;
vorhergehende jeweils aus dem Datenregister in das Sende-Schieberegister&lt;br /&gt;
übernommen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Zu diesem Zweck können wir uns für&#039;s Erste eine einfache Funktion basteln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
void SendeString (char *szBuf)&lt;br /&gt;
{&lt;br /&gt;
  while (*szBuf++) {&lt;br /&gt;
     UDR=*szBuf;&lt;br /&gt;
     loop_until_bit_is_set (USR, UDRE);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist jetzt insofern etwas kritisch, dass wir während dem Senden des&lt;br /&gt;
Strings nicht mehr auf andere Ereignisse reagieren können. Dies wird aber&lt;br /&gt;
ohnehin erst dann ein Thema, wenn wir mit unserem Programm gleichzeitig Senden&lt;br /&gt;
und Empfangen wollen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wir werden zu einem späteren Zeitpunkt Lösung für dieses Problem finden (Interrupt).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (prinf etc.) im [[AVR-Tutorial - UART]].&lt;br /&gt;
* [http://homepage.sunrise.ch/mysunrise/pfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&lt;br /&gt;
= Analoge Ein- und Ausgabe =&lt;br /&gt;
&lt;br /&gt;
Leider können wir mit unseren Controllern keine analogen Werte direkt&lt;br /&gt;
verarbeiten. Dazu bedarf es jeweils einer Umwandlung des analogen Signals in&lt;br /&gt;
einen digitalen Zahlenwert. Je nachdem, ob wir Daten einlesen oder ausgeben&lt;br /&gt;
wollen reden wir dabei von &#039;&#039;&#039; DAC&#039;&#039;&#039; oder &#039;&#039;&#039;ADC&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== ADC (Analog Digital Converter) ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; wandelt analoge Signale in digitale Werte um welche vom Controller&lt;br /&gt;
interpretiert werden können. Einige AVR-Typen haben bereits einen oder mehrere&lt;br /&gt;
solcher &#039;&#039;&#039;ADC&#039;&#039;&#039;&#039;s eingebaut, bei den kleineren AVR&#039;s müssen wir uns anders aus der&lt;br /&gt;
Affäre ziehen. Die Genauigkeit, mit welcher ein analoges Signal aufgelöst&lt;br /&gt;
werden kann wird durch die Auflösung des &#039;&#039;&#039; ADC&#039;&#039;&#039; in Anzahl Bits angegeben, man&lt;br /&gt;
hört bzw. liest jeweils von 8-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder 10-Bit &#039;&#039;&#039; ADC&#039;&#039;&#039; oder noch höher.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039; ADC&#039;&#039;&#039; mit 8 Bit Auflösung kann somit das analoge Signal mit einer Genauigkeit&lt;br /&gt;
von 1/255 des Maximalwertes darstellen. Wenn wir nun mal annehmen, wir hätten&lt;br /&gt;
eine Spannung zwischen 0 und 5 Volt und eine Auflösung von 3 Bit, dann könnten&lt;br /&gt;
die Werte 0V, 0.625V, 1.25, 1.875V, 2.5V, 3.125V, 3.75, 4.375,&amp;amp;nbsp; 5V&lt;br /&gt;
daherkommen, siehe dazu folgende Tabelle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Eingangsspannung am ADC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Entsprechender Messwert&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0...0.625V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.625...1.25V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.25...1.875V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.875...2.5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2.5...3.125V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.125...3.75V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3.75...4.375V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4.375...5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 8&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angaben sind natürlich nur ungefähr. Je höher nun die Auflösung des &#039;&#039;&#039;&lt;br /&gt;
ADC&#039;&#039;&#039; ist, also je mehr Bits er hat, um so genauer kann der Wert erfasst werden.&lt;br /&gt;
&lt;br /&gt;
=== Messen eines Widerstandes ===&lt;br /&gt;
&lt;br /&gt;
Wir wollen hier einmal die wohl einfachste Methode zur Erfassung eines&lt;br /&gt;
analogen Wertes realisieren und zwar das Messen eines veränderlichen&lt;br /&gt;
Widerstandes wie z.B. eines Potentiometers.&lt;br /&gt;
&lt;br /&gt;
Man stelle sich vor, wir schalten einen Kondensator in Reihe zu einem&lt;br /&gt;
Widerstand zwischen die Versorgungsspannung und Masse und dazwischen nehmen wir&lt;br /&gt;
das Signal ab und führen es auf einen der Pins an unserem Controller, genau so&lt;br /&gt;
wie es in folgender Grafik dargestellt ist.&lt;br /&gt;
&lt;br /&gt;
[[Image:Poti.gif]]&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun den Pin des AVR als Ausgang schalten und auf&lt;br /&gt;
Logisch 1 (HIGH) legen, dann liegt an beiden Platten des Kondensators &#039;&#039;&#039; Vcc&#039;&#039;&#039; an und&lt;br /&gt;
dieser wird entladen (Klingt komisch, mit &#039;&#039;&#039; Vcc&#039;&#039;&#039; entladen, ist aber so).&amp;lt;br /&amp;gt;&lt;br /&gt;
Nachdem nun der Kondensator genügend entladen ist schalten wir einfach den Pin&lt;br /&gt;
als Eingang wodurch dieser hochohmig wird. Der Kondensator lädt sich jetzt&lt;br /&gt;
über das Poti auf, dabei steigt der Spannungsabfall über dem Kondensator und&lt;br /&gt;
derjenige über dem Poti sinkt. Fällt nun der Spannungsabfall über dem Poti&lt;br /&gt;
unter die&amp;amp;nbsp; Thresholdspannung des Eingangspins (2/5 Vcc, also ca. 2V), dann schaltet der&lt;br /&gt;
Eingang von HIGH auf LOW um. Wenn wir nun messen (zählen), wie lange es dauert,&lt;br /&gt;
bis der Kondensator so weit geladen ist, dann haben wir einen ungefähren Wert&lt;br /&gt;
der Potentiometerstellung.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der 220 Ohm Widerstand dient dem Schutz des Controllers. Wenn nämlich sonst die&lt;br /&gt;
Potentiometerstellung auf Maximum steht (0 Ohm), dann würde in den Eingang des&lt;br /&gt;
Controllers ein viel zu hoher Strom fliessen und der AVR würde in Rauch&lt;br /&gt;
aufgehen.&lt;br /&gt;
&lt;br /&gt;
Dies ist meines Wissens die einzige Schaltung zur Erfassung von&lt;br /&gt;
Analogwerten, welche mit nur einem einzigen Pin auskommt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit einem weiteren Eingangspin und ein wenig Software können wir auch eine&lt;br /&gt;
Kalibrierung realisieren, um den Messwert in einen vernünftigen Bereich (z.B:&lt;br /&gt;
0...100 % oder so) umzurechnen.&lt;br /&gt;
&lt;br /&gt;
Wer Lust hat, sich selber mal an ein solches Programm&lt;br /&gt;
heranzuwagen, der sollte das jetzt tun. Für diejenigen, die es gern schnell&lt;br /&gt;
mögen, hier das Beispielprogramm, welches den UART-Printf aus den&lt;br /&gt;
vorangegangenen Kapiteln benötigt, inl. Makefile:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Poti.c Poti.c ]&lt;br /&gt;
| Hauptprogramm.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.c Pot.c]&lt;br /&gt;
| Separate Routine zur Ermittlung des Messwertes.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/Pot.h Pot.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.c UartPrintF.c]&lt;br /&gt;
| Für die Debugausgabe auf den UART.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Allgemein/UartPrintF.h UartPrintF.h]&lt;br /&gt;
| Zugehörige Headerdatei.&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.mypage.bluewin.ch/ch_schifferle/AtmelC/Uebungen/Poti/makefile Makefile]&lt;br /&gt;
| Makefile.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Nachdem das Programm auf den AVR geladen wurde muss dieser&lt;br /&gt;
kalibriert werden. Dazu wird der Kalibrierungsschalter geschlossen und das Poti&lt;br /&gt;
einige Male zwischen minimaler und maximaler Stellung hin und her gedreht. Dabei&lt;br /&gt;
werden die jeweiligen Maximalwerte bestimmt. Wenn der Kalibrierschalter wieder&lt;br /&gt;
geöffnet wird werden die Kalibrierungsdaten in&#039;s EEPROM des AVR geschrieben,&lt;br /&gt;
damit die Prozedur nicht nach jedem Reset wiederholt werden muss.&lt;br /&gt;
&lt;br /&gt;
Auf Pin 4 habe ich noch ein Triggersignal gelegt, welches auf&lt;br /&gt;
HIGH geht wenn die Messung beginnt und auf LOW, wenn der Messvorgang beendet&lt;br /&gt;
wird. Mit Hilfe dieses Signals kann der Vorgang wunderschön auf einem&lt;br /&gt;
Oszillographen dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== ADC über Komparator ===&lt;br /&gt;
&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;
[[Image:ADC ueber Komparator.gif]]&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.&amp;lt;br /&amp;gt;&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 Mass 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&lt;br /&gt;
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&lt;br /&gt;
direkt Spannungen gemessen werden können.&lt;br /&gt;
&lt;br /&gt;
=== Der ADC im AVR ===&lt;br /&gt;
&lt;br /&gt;
Wenn es einmal etwas genauer sein soll, dann müssen wir auf einen AVR mit eingebautem &#039;&#039;&#039;ADC-Wandler&#039;&#039;&#039; zurückgreifen. Wir wollen hier einmal den AT90S8535 besprechen, welcher über 8 ADC-Kanäle verfügt. (TODO: nur ein Wandler, Mulitiplexbetrieb, nur eine Pin zur gleichen Zeit)&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.  &lt;br /&gt;
&lt;br /&gt;
Verfügt der AVR über eine interne Referenzspannung (Datenblatt typisch 2,56 oder 1,1V je nach AVR), bleibt der &#039;&#039;Anschluss AREF&#039;&#039; unbeschaltet und man stellt die ADC-Register so ein, dass die interne Referenzspannung als &amp;quot;AREF&amp;quot; genutzt wird. Ansonsten ist eine externe Referenzspannung von maximal &#039;&#039;&#039;Vcc&#039;&#039;&#039; an den Abschluss &#039;&#039;&#039;AREF&#039;&#039;&#039; anzulegen. 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) ====&lt;br /&gt;
&lt;br /&gt;
In dieser Betriebsart wird der Wandler bei Bedarf vom Programm angestossen für jeweils eine Messung.&lt;br /&gt;
&lt;br /&gt;
==== Frei laufend (Free Running) ====&lt;br /&gt;
&lt;br /&gt;
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, welche hier aufgelistet werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADCSR&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.&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADEN&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;En&#039;&#039;&#039;able&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt werden, um den &#039;&#039;&#039; ADC&#039;&#039;&#039; überhaupt zu aktivieren.&lt;br /&gt;
Wenn das Bit nicht gesetzt ist können die Pin&#039;s wie normale&lt;br /&gt;
I/O-Pins verwendet werden.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADSC&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;S&#039;&#039;&#039;tart &#039;&#039;&#039;C&#039;&#039;&#039;onversion&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird ein Messvorgang gestartet. In der frei laufenden&lt;br /&gt;
Betriebsart muss das Bit gesetzt werden, um die kontinuierliche&lt;br /&gt;
Messung zu aktivieren.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Bit&amp;amp;nbsp; nach dem Setzen des &#039;&#039;&#039;ADEN&#039;&#039;&#039;-Bits zum ersten Mal&lt;br /&gt;
gesetzt wird führt der Controller zuerst eine zusätzliche Wandlung&lt;br /&gt;
und erst dann die eigentliche Wandlung aus. Diese zusätzliche&lt;br /&gt;
Wandlung wird zu Initialisierungszwecken durchgeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit bleibt nun so lange auf 1, bis die Umwandlung abgeschlossen&lt;br /&gt;
ist, im Initialisierungsfall entsprechend bis die zweite Umwandlung&lt;br /&gt;
erfolgt ist und geht danach auf 0.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;Fr&#039;&#039;&#039;ee Running Select&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesem Bit wird die Betriebsart eingestellt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Eine logische 1 aktiviert den frei laufenden Modus. Der &#039;&#039;&#039; ADC&#039;&#039;&#039; misst&lt;br /&gt;
nun ständig den ausgewählten Kanal und schreibt den gemessenen&lt;br /&gt;
Wert in das &#039;&#039;&#039; ADC Data Register&#039;&#039;&#039;.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIF&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;F&#039;&#039;&#039;lag&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom &#039;&#039;&#039; ADC&#039;&#039;&#039; gesetzt wenn eine Umwandlung erfolgt und das&lt;br /&gt;
&#039;&#039;&#039;ADC Data Register&#039;&#039;&#039; aktualisiert ist.&amp;lt;br /&amp;gt;&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&lt;br /&gt;
wird der &#039;&#039;&#039; ADC Interrupt&#039;&#039;&#039; ausgelöst und die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen..&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit wird automatisch gelöscht wenn die&lt;br /&gt;
Interrupt-Behandlungsroutine aufgerufen wird. Es kann jedoch auch&lt;br /&gt;
gelöscht werden, indem ein logisches 1 in das Register geschrieben&lt;br /&gt;
wird (So steht&#039;s in der AVR-Doku).&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ADIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;AD&#039;&#039;&#039;C &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist und ebenso das &#039;&#039;&#039; I-Bit&#039;&#039;&#039; im Statusregister&lt;br /&gt;
&#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&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
ADPS0&#039;&#039;&#039;&lt;br /&gt;
| &#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&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese Bits bestimmen den Teilungsfaktor zwischen der Taktfrequenz&lt;br /&gt;
und dem Eingangstakt des &#039;&#039;&#039;ADC&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der &#039;&#039;&#039; ADC&#039;&#039;&#039; benötigt einen eigenen Takt, welchen er sich selber aus der&lt;br /&gt;
CPU-Taktfreqenz erzeugt. Der &#039;&#039;&#039;ADC&#039;&#039;&#039;-Takt sollte zwischen 50 und 200kHz&lt;br /&gt;
sein.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorteiler muss also so eingestellt werden, dass die&lt;br /&gt;
CPU-Taktfrequenz dividiert durch den Teilungsfaktor einen Wert&lt;br /&gt;
zwischen 50-200kHz ergibt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Bei einer CPU-Taktfrequenz von 4MHz beispielsweise rechnen wir&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmin=CLK/200kHz=4000000/200000=&#039;&#039;&#039;20&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp; TFmax=CLK/50kHz=4000000/50000=&#039;&#039;&#039;80&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Somit kann hier der Teilungsfaktor 32 oder 64 verwendet werden. Im&lt;br /&gt;
Interesse der schnelleren Wandlungszeit werden wir hier den Faktor&lt;br /&gt;
32 einstellen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ADPS0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Teilungsfaktor&#039;&#039;&#039;&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 2&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;
| align=&amp;quot;center&amp;quot; | 4&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;
| align=&amp;quot;center&amp;quot; | 8&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;
| align=&amp;quot;center&amp;quot; | 16&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;
| align=&amp;quot;center&amp;quot; | 32&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;
| align=&amp;quot;center&amp;quot; | 64&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;
| align=&amp;quot;center&amp;quot; | 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;pre class=&amp;quot;code&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-Operatorprioritaet sichergestellt)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
x = ADCW; // je nach AVR auch x = ADC (siehe avr/ioxxx.h)&lt;br /&gt;
&amp;lt;/pre&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MUX0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;MUX2&amp;lt;br /&amp;gt;&lt;br /&gt;
...&amp;lt;br /&amp;gt;&lt;br /&gt;
MUX0&#039;&#039;&#039;&lt;br /&gt;
| Mit diesem 3 Bits wird der zu messende Kanal bestimmt.&lt;br /&gt;
Es wird einfach die entsprechende Pinnummer des Ports&lt;br /&gt;
eingeschrieben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Register beschrieben wird, während dem eine Umwandlung&lt;br /&gt;
läuft, so wird zuerst die aktuelle Umwandlung auf dem bisherigen&lt;br /&gt;
Kanal beendet. Dies ist vor allem beim frei laufenden Betrieb zu&lt;br /&gt;
berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
Meine Empfehlung ist deswegen klar diese, dass der frei laufende&lt;br /&gt;
Betrieb nur bei einem einzelnen zu verwendenden Analogeingang&lt;br /&gt;
verwendet werden sollte, wenn man sich Probleme bei der Umschalterei&lt;br /&gt;
ersparen will.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Aktivieren 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;ADCSR&#039;&#039;&#039;-Register&lt;br /&gt;
setzen. Im gleichen Schritt legen wir auch gleich die Betriebsart fest. &lt;br /&gt;
&lt;br /&gt;
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). D.h. das Eingangssignal darf diese Spannung nicht überschreiten, gegebenenfalls mit Spannungsteiler konditionieren. Ergebnis der Routine ist der ADC-Wert, also 0 für 0-Volt und 1024 für V_ref-Volt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define CHANNELOFFSET 4&lt;br /&gt;
&lt;br /&gt;
uint16_t ReadChannel(uint8_t channel)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  &lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0);    // Frequenzvorteiler setzen auf 8 (1)&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADEN);     //  ADC aktivieren (1)&lt;br /&gt;
&lt;br /&gt;
  ADMUX = CHANNELOFFSET+channel; // Kanal waehlen&lt;br /&gt;
  ADMUX |= (1&amp;lt;&amp;lt;REFS1) | (1&amp;lt;&amp;lt;REFS0); // interne Referenzspannung nutzen &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;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC);        // eine ADC-Wandlung &lt;br /&gt;
  while(!(ADCSRA &amp;amp; 0x10));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
        &lt;br /&gt;
  result = 0;&lt;br /&gt;
&lt;br /&gt;
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */&lt;br /&gt;
  for(i=0;i&amp;lt;4;i++)&lt;br /&gt;
  {&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;ADIF)));    // auf Abschluss der Konvertierung warten (ADIF-bit)&lt;br /&gt;
	result += ADC;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);      // ADC deaktivieren (2)&lt;br /&gt;
&lt;br /&gt;
  result &amp;gt;&amp;gt;= 2;     // Summe durch vier teilen = arithm. Mittelwert&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird bei jedem Aufruf der ADC aktiviert und nach der Wandlung wieder abgeschaltet, das spart Strom. Will man dies nicht, verschiebt man die mit (1) gekenntzeichneten Zeilen in eine Funktion adc_init() o.ä. und löscht die mit (2) markierten Zeilen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wenn wir frei laufend arbeiten wollen setzen wir zusätzlich das &#039;&#039;&#039;ADFR&#039;&#039;&#039;-Bit. Bei&lt;br /&gt;
Bedarf wird auch gleich der Teilungsfaktor eingestellt.&lt;br /&gt;
&lt;br /&gt;
TODO: fix&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;// Teilungsfaktor auf 8 und ADC aktivieren&amp;lt;br /&amp;gt;&lt;br /&gt;
// Nicht frei laufend&amp;lt;br /&amp;gt;&lt;br /&gt;
outp ((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um nun eine einzelne Messung, sagen wir mal an Pin 3, durchzuführen schalten wir den Kanal ein, starten die Wandlung und warten, bis der &#039;&#039;&#039;ADC&#039;&#039;&#039; die Beendigung meldet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp ((1&amp;lt;&lt;br /&gt;
sbi (ADCSR, ADSC);&amp;lt;br /&amp;gt;&lt;br /&gt;
while (bit_is_set (ADCSR, ADSC)) /* Nur warten */;&amp;lt;br /&amp;gt;&lt;br /&gt;
x = __inw (ADCL);  ODER x=ADCW        // Wert auslesen&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;#FF0000&amp;quot;&amp;gt;Da ich leider bisher noch kein Experimentierboard für&lt;br /&gt;
den 8535 gebastelt habe kann ich euch auch keine erprobte Übung anbieten.&amp;lt;/font&amp;gt;&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DAC (Digital Analog Converter) ==&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe eines &#039;&#039;&#039; DAC&#039;&#039;&#039; können wir nun auch Analogsignale ausgeben. Es gibt hier&lt;br /&gt;
mehrere Verfahren. Wenn wir beim &#039;&#039;&#039; ADC&#039;&#039;&#039; die Möglichkeit haben, mit externen&lt;br /&gt;
Komponenten zu operieren müssen wir bei der &#039;&#039;&#039;DAC&#039;&#039;&#039;-Wandlung mit dem auskommen, was&lt;br /&gt;
der Controller selber zu bieten hat.&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 &#039;&#039;&#039; PWM&#039;&#039;&#039; 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&lt;br /&gt;
wir setzen den Ausgang auf LOW wonach dann &#039;&#039;&#039; 0V&#039;&#039;&#039; am Ausgang liegt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Was passiert aber nun, wenn wir periodisch mit einer festen Frequenz zwischen&lt;br /&gt;
HIGH und LOW umschalten?&amp;lt;br /&amp;gt;&lt;br /&gt;
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 geometrischen Mittelwert, der je nach Pulsbreite&lt;br /&gt;
kleiner oder grösser 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&lt;br /&gt;
RC-Glied filtern dann haben wir schon eine entsprechende Gleichspannung erzeugt.&lt;br /&gt;
&lt;br /&gt;
Mit den AVR&#039;s können wir direkt &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signale erzeugen. Dazu&lt;br /&gt;
dient der 16-Bit Zähler, welcher im sogenannten &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus betrieben werden&lt;br /&gt;
kann.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Hinweis:&lt;br /&gt;
| In den folgenden Überlegungen wird als Controller der&lt;br /&gt;
90S2313 vorausgesetzt. Die Theorie ist allerdings bei anderen&lt;br /&gt;
AVR-Controllern vergleichbar, die Pinbelegung allerdings nicht unbedingt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den &#039;&#039;&#039;PWM&#039;&#039;&#039;-Modus zu aktivieren müssen im Timer/Counter1 Control&lt;br /&gt;
Register A &#039;&#039;&#039;TCCR1A&#039;&#039;&#039; die Pulsweiten-Modulatorbits &#039;&#039;&#039; PWM10&#039;&#039;&#039; bzw. &#039;&#039;&#039; PWM11&#039;&#039;&#039; entsprechend&lt;br /&gt;
nachfolgender Tabelle gesetzt werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;
| PWM-Modus des Timers ist nicht aktiv.&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;
| 8-Bit PWM.&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;
| 9-Bit PWM.&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;
| 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. Die&lt;br /&gt;
Obergrenze hängt davon ab, ob wir mit 8, 9 oder 10-Bit PWM arbeiten wollen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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 heisst dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: fix --&amp;gt;&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;outp&lt;br /&gt;
((1&amp;lt;&#039;&#039;&#039;&amp;lt;/font&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 (Vorzähler) 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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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;!-- TODO: fix, irgenwas ist bei der &amp;quot;wiki-konvertierung&amp;quot; wohl schief gelaufen --&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1&amp;lt;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&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;
Wir können dazu den von der Bibliothek zur Verfügung gestellten Befehl zum&lt;br /&gt;
Schreiben von 16-Bit Registern verwenden, als Portadresse wird das Low-Byte des&lt;br /&gt;
Ports verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;__outw (xxx, OCR1AL);&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
OCR1A = xxx;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die folgende Grafik soll den Zusammenhang zwischen dem&lt;br /&gt;
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 nur ein einzelnes &#039;&#039;&#039;PWM&#039;&#039;&#039;-Signal mit dieser Methode generieren.&lt;br /&gt;
&lt;br /&gt;
= Die Timer/Counter des AVR =&lt;br /&gt;
&lt;br /&gt;
Die heutigen Mikrocontroller und insbesondere die RISC-AVR&#039;s sind für viele&lt;br /&gt;
Steuerungsaufgaben natürlich viel zu schnell. Wenn wir beispielsweise eine LED&lt;br /&gt;
oder Lampe blinken lassen wollen können wir selbstverständlich nicht die&lt;br /&gt;
CPU-Frequenz verwenden da ja dann nichts mehr vom Blinken zu bemerken wäre.&lt;br /&gt;
&lt;br /&gt;
Wir brauchen also eine Möglichkeit, die Taktfrequenz auf vernünftige Werte&lt;br /&gt;
herunter zu brechen. Selbstverständlich sollte die resultierende Frequenz dann&lt;br /&gt;
auch noch einigermassen genau und stabil sein.&lt;br /&gt;
&lt;br /&gt;
Hier kommen die im AVR vorhandenen Timer/Counter zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Anwendungsgebiet ist die Zählung von Signalen, welche über&lt;br /&gt;
einen I/O-Pin zugeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ausführungen beziehen sich auch den AT90S2313. Für andere&lt;br /&gt;
Modelltypen müsst ihr euch die allenfalls notwendigen Anpassungen aus den&lt;br /&gt;
Datenblättern der entsprechenden Controller raus lesen.&lt;br /&gt;
&lt;br /&gt;
Wir unterscheiden grundsätzlich zwischen 8-Bit Timern, welche eine&lt;br /&gt;
Auflösung von 256 aufweisen und 16-Bit Timern mit (logischerweise) einer&lt;br /&gt;
Auflösung von 65536.&lt;br /&gt;
&lt;br /&gt;
Als Eingangstakt für die Timer/Counter kann entweder die CPU-Taktfrequenz,&lt;br /&gt;
der Vorteiler-Ausgang oder ein an einen I/O-Pin angelegtes Signal verwendet&lt;br /&gt;
werden. Wenn ein externes Signal verwendet wird, so darf dessen Frequenz nicht&lt;br /&gt;
höher sein als die Hälfte des CPU-Taktes.&lt;br /&gt;
&lt;br /&gt;
=== Der Vorzähler (Prescaler) ===&lt;br /&gt;
&lt;br /&gt;
Beide Timer/Counter werden im Timerbetrieb über den gleichen Vorzähler&lt;br /&gt;
versorgt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Vorzähler dient dazu, den CPU-Takt vorerst mal runter zu brechen auf eine&lt;br /&gt;
wählbare Teilung. Die so geteilte Frequenz wird den Eingängen der Timer&lt;br /&gt;
zugeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn wir mit einer einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024&lt;br /&gt;
einstellen wird also der Timer mit einer Frequenz von 4 MHz / 1024, also mit ca.&lt;br /&gt;
4 kHz versorgt. Wenn also der Timer läuft, so wird das Daten- bzw.&lt;br /&gt;
Zählregister mit dieser Frequenz inkrementiert.&lt;br /&gt;
&lt;br /&gt;
== 8-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Alle AVR-Modelle weisen mindestens einen, teilweise sogar zwei 8-Bit Timer&lt;br /&gt;
auf.&lt;br /&gt;
&lt;br /&gt;
Der 8-Bit Timer wird über folgende Register angesprochen:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS02&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS02&amp;lt;br /&amp;gt;&lt;br /&gt;
CS01&amp;lt;br /&amp;gt;&lt;br /&gt;
CS00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS02&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS01&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS00&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, fallende 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 &#039;&#039;&#039;TO&#039;&#039;&#039;, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin &#039;&#039;&#039;TO&#039;&#039;&#039; verwendet wird, so wird&lt;br /&gt;
ein Flankenwechsel auch erkannt, wenn der Pin &#039;&#039;&#039;TO&#039;&#039;&#039; als Ausgang&lt;br /&gt;
geschaltet ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 8-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 255 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um nun also den Timer0 in Betrieb zu setzen und ihn mit einer Frequenz von 1/1024-tel des CPU-Taktes zählen zu lassen schreiben wir die folgende&lt;br /&gt;
Befehlszeile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- TODO: fix --&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;outp ((1&amp;lt;&amp;lt;CS02) | (1&amp;lt;&amp;lt;CS00), TCCR0);&amp;lt;/font&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Zähler zählt nun von 0 an aufwärts bis 255 um dann wieder&lt;br /&gt;
bei 0 zu beginnen. Bei jedem Überlauf von 255 auf 0 wird das Timer Overflow&lt;br /&gt;
Flag &#039;&#039;&#039;[http://en.wikipedia.org#TOV0 TOV0]&#039;&#039;&#039; im Timer Interrupt Flag &#039;&#039;&#039;[http://en.wikipedia.org#TIFR TIFR]&#039;&#039;&#039;&lt;br /&gt;
Register gesetzt und, falls so konfiguriert, ein entsprechender Interrupt&lt;br /&gt;
ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== 16-Bit Timer/Counter ==&lt;br /&gt;
&lt;br /&gt;
Viele AVR-Modelle besitzen ausser den 8-Bit Timers auch einen oder sogar zwei&lt;br /&gt;
(einige ATMega-Modelle) 16-Bit Timer.&lt;br /&gt;
&lt;br /&gt;
Die 16-Bit Timer/Counter sind wesentlich komplexer aufgebaut als die 8-Bit&lt;br /&gt;
Timer/Counter, bieten dafür aber auch viel mehr Möglichkeiten, als da sind:&lt;br /&gt;
&lt;br /&gt;
* [http://en.wikipedia.org#Die%20PWM-Betriebsart Erzeugung eines pulsweitenmodulierten Ausgangssignals] ([http://en.wikipedia.org/Analoge_Ein_Ausgabe.htm#PWM PWM]).&lt;br /&gt;
* [http://en.wikipedia.org#Vergleichswert-%DCberpr%FCfung Vergleichswert-Überprüfung] mit Erzeugung eines Ausgangssignals (Output Compare Match).&lt;br /&gt;
* [http://en.wikipedia.org#Einfangen%20eines%20Eingangssignals Einfangen eines Eingangssignals] mit Speicherung des aktuellen Zählerwertes (Input Capturing), mit zuschaltbarer Rauschunterdrückung (Noise Filtering).&lt;br /&gt;
&lt;br /&gt;
Folgende Register sind dem Timer/Counter 1 zugeordnet:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
&#039;&#039;&#039;A&#039;&#039;&#039; Timer &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem und dem folgenden Register stellen wir ein, wie wir den Timer/Counter verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;COM1A0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;COM1A1&amp;lt;br /&amp;gt;&lt;br /&gt;
COM1A0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Co&#039;&#039;&#039;mpare &#039;&#039;&#039;M&#039;&#039;&#039;atch Control Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 2 Bits bestimmen die Aktion, welche am Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039;&lt;br /&gt;
ausgeführt werden soll, wenn der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 den Wert des Vergleichsregisters erreicht, also ein so genannter&lt;br /&gt;
Compare Match auftritt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) muss mit dem&lt;br /&gt;
Datenrichtungsregister als Ausgang konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Das Signal am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird invertiert&lt;br /&gt;
(Toggle).&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 0 gesetzt.&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;
| Der Output Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird auf 1 gesetzt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
| In der PWM-Betriebsart haben diese Bits eine andere&lt;br /&gt;
Funktion.&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | COM1A0&lt;br /&gt;
| Resultat&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Output-Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; wird nicht angesteuert.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 0&lt;br /&gt;
gesetzt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister&lt;br /&gt;
erreicht so wird der Pin auf 1 gesetzt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man nennt dies nicht invertierende PWM.&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;
| Wird beim Hochzählen der Wert im&lt;br /&gt;
Vergleichsregister erreicht so wird der Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; auf 1&lt;br /&gt;
gesetzt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wird beim Herunterzählen der Wert im Vergleichsregister&lt;br /&gt;
erreicht so wird der Pin auf 0 gesetzt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Man nennt dies invertierende PWM.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;PWM11&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;&#039;PWM10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;PWM&#039;&#039;&#039; Mode Select Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Mit diesen 2 Bits wird die PWM-Betriebsart des Timer/Counter 1&lt;br /&gt;
gesteuert.&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | PWM10&lt;br /&gt;
| Resultat&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;
| Die PWM-Betriebsart ist nicht aktiviert. Timer/Counter&lt;br /&gt;
1 arbeitet als normaler Timer bzw. Zähler.&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;
| 8-Bit PWM Betriebsart aktivieren.&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;
| 9-Bit PWM Betriebsart aktivieren.&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;
| 10-Bit PWM Betriebsart aktivieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039;&lt;br /&gt;
Timer &#039;&#039;&#039;1&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS12&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;CS10&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICNC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;N&#039;&#039;&#039;oise &#039;&#039;&#039;C&#039;&#039;&#039;anceler&lt;br /&gt;
(4 CKs) Timer/Counter 1&amp;lt;br /&amp;gt;&lt;br /&gt;
oder auf Deutsch Rauschunterdrückung des Eingangssignals.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist und mit dem Input Capture Signal&lt;br /&gt;
gearbeitet wird so werden nach der Triggerung des Signals mit der&lt;br /&gt;
entsprechenden Flanke (steigend oder fallend) am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039;&lt;br /&gt;
jeweils 4 Messungen mit der CPU-Frequenz des Eingangssignals&lt;br /&gt;
abgefragt. Nur dann, wenn alle 4 Messungen den gleichen Zustand&lt;br /&gt;
aufweisen gilt das Signal als erkannt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICES1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;E&#039;&#039;&#039;dge &#039;&#039;&#039;S&#039;&#039;&#039;elect&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Mit diesem Bit wird bestimmt, ob die steigende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=1)&lt;br /&gt;
oder fallende (&#039;&#039;&#039;ICES1&#039;&#039;&#039;=0) Flanke zur Auswertung des Input&lt;br /&gt;
Capture Signals an Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; heran gezogen wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CTC1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ounter&lt;br /&gt;
on Compare Match Timer/Counter &#039;&#039;&#039;1&amp;lt;br /&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Wenn dieses Bit gesetzt ist so wird nach einer Übereinstimmung&lt;br /&gt;
des Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; mit dem&lt;br /&gt;
Vergleichswert in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039; das Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
auf 0 gesetzt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Da die Übereinstimmung im Takt nach dem Vergleich behandelt wird&lt;br /&gt;
ergibt sich je nach eingestelltem Vorzähler ein etwas anderes&lt;br /&gt;
Zählverhalten:&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler auf 1 gestellt ist und C der jeweilige&lt;br /&gt;
Zählerwert ist, dann nimmt das Datenregister, im CPU-Takt&lt;br /&gt;
betrachtet, folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2 | C-1 | C | 0 | 1 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn der Vorteiler z.B. auf 8 eingestellt ist, dann nimmt das&lt;br /&gt;
Datenregister folgende Werte an:&amp;lt;br /&amp;gt;&lt;br /&gt;
... | C-2, C-2, C-2, C-2, C-2, C-2, C-2, C-2 | C-1, C-1, C-1, C-1,&lt;br /&gt;
C-1, C-1, C-1, C-1 | C, 0, 0, 0, 0, 0, 0, 0 |...&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart hat dieses Bit keine Funktion.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;CS12&amp;lt;br /&amp;gt;&lt;br /&gt;
CS11&amp;lt;br /&amp;gt;&lt;br /&gt;
CS10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;S&#039;&#039;&#039;elect Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Quelle für den Timer/Counter:&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS12&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS11&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | CS10&lt;br /&gt;
| Resultat&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;
| Stopp, Der Timer/Counter wird angehalten.&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;
| CPU-Takt&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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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;
| CPU-Takt / 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 TO, fallende 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 T0, steigende Flanke&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn als Quelle der externe Pin T0 verwendet wird, so wird ein&lt;br /&gt;
Flankenwechsel auch erkannt, wenn der Pin T0 als Ausgang geschaltet&lt;br /&gt;
ist.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TCNT1H&amp;lt;br /&amp;gt;&lt;br /&gt;
TCNT1L&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/&#039;&#039;&#039;C&#039;&#039;&#039;ou&#039;&#039;&#039;nt&#039;&#039;&#039;er Daten Register Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses ist als 16-Bit Aufwärtszähler mit Schreib- und Lesezugriff&lt;br /&gt;
realisiert. Wenn der Zähler den Wert 65535 erreicht hat beginnt er beim&lt;br /&gt;
nächsten Zyklus wieder bei 0.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der PWM-Betriebsart wird das Register als Auf/Ab-Zähler verwendet,&lt;br /&gt;
d.h. der Wert steigt zuerst von 0 bis er den Überlauf von 65535 auf 0&lt;br /&gt;
erreicht hat. Dann zählt das Register rückwärts wiederum bis 0.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register&lt;br /&gt;
verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts&lt;br /&gt;
gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs&lt;br /&gt;
auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem&lt;br /&gt;
Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst&lt;br /&gt;
danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle&lt;br /&gt;
Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register&lt;br /&gt;
und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau&lt;br /&gt;
die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCR1H&amp;lt;br /&amp;gt;&lt;br /&gt;
OCR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Wert im Output Compare Register wird ständig mit dem aktuellen&lt;br /&gt;
Wert im Datenregister TCNT1H/TCNT1L verglichen. Stimmen die beiden Werte&lt;br /&gt;
überein so wird ein sogenannter Output Compare Match ausgelöst. Die&lt;br /&gt;
entsprechenden Aktionen werden über die Timer/Counter 1 Control und&lt;br /&gt;
Status Register eingestellt..&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen des Registers wird von der CPU ein internes TEMP-Register&lt;br /&gt;
verwendet. Das gleiche Register wird auch verwendet, wenn auf &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
oder &#039;&#039;&#039;ICR1&#039;&#039;&#039; zugegriffen wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Deshalb müssen vor dem Zugriff auf eines dieser Register alle Interrupts&lt;br /&gt;
gesperrt werden, weil sonst die Möglichkeit des gleichzeitigen Zugriffs&lt;br /&gt;
auf das Temporärregister gegeben ist, was natürlich zu fehlerhaftem&lt;br /&gt;
Verhalten des Programms führt.. Zudem muss zuerst &#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und erst&lt;br /&gt;
danach &#039;&#039;&#039;TCNT1H&#039;&#039;&#039; ausgelesen werden.&lt;br /&gt;
&lt;br /&gt;
Wenn in das Register geschrieben werden soll müssen ebenfalls alle&lt;br /&gt;
Interrrupts gesperrt werden. Dann muss zuerst das &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;-Register&lt;br /&gt;
und erst danach das &#039;&#039;&#039;TCNT1L&#039;&#039;&#039;-Register geschrieben werden, also genau&lt;br /&gt;
die umgekehrte Reihenfolge wie beim Lesen des Registers.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&amp;lt;br /&amp;gt;&lt;br /&gt;
ICR1L&#039;&#039;&#039;&lt;br /&gt;
| Timer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;R&#039;&#039;&#039;egister&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&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; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;MSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;LSB&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICR1&#039;&#039;&#039;&#039;&#039;&#039;L&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| 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; | &amp;amp;nbsp;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das Input Capture Register ist ein 16-Bit Register mit Lesezugriff. Es&lt;br /&gt;
kann nicht beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn am Input Capture Pin &#039;&#039;&#039;ICP&#039;&#039;&#039; die gemäss Einstellungen im &#039;&#039;&#039;TCCR1B&#039;&#039;&#039;&lt;br /&gt;
definierte Flanke erkannt wird so wird der aktuelle Inhalt des&lt;br /&gt;
Datenregisters &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; sofort in dieses Register&lt;br /&gt;
kopiert und das Input Capture Flag &#039;&#039;&#039;ICF1&#039;&#039;&#039; im Timer Interrupt Flag&lt;br /&gt;
Register &#039;&#039;&#039;TIFR&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wie bereits oben erwähnt müssen vor dem Zugriff auf dieses Register&lt;br /&gt;
alle Interrupts gesperrt werden. Zudem müssen Low- und Highbyte des&lt;br /&gt;
Registers in der richtigen Reihenfolge bearbeitet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| Lesen:&lt;br /&gt;
| &#039;&#039;&#039;ICR1L&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1H&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| Schreiben:&lt;br /&gt;
| &#039;&#039;&#039;ICR1H&#039;&#039;&#039; -&amp;gt; &#039;&#039;&#039;ICR1L&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Die PWM-Betriebsart ===&lt;br /&gt;
&lt;br /&gt;
Wenn der Timer/Counter 1 in der PWM-Betriebsart betrieben wird so bilden das&lt;br /&gt;
Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; und das Vergleichsregister &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
einen 8-, 9- oder 10-Bit, frei laufenden PWM-Modulator, welcher als PWM-Signal&lt;br /&gt;
am &#039;&#039;&#039;OC1&#039;&#039;&#039;-Pin (&#039;&#039;&#039;PB3&#039;&#039;&#039; beim 2313) abgegriffen werden kann. Das&lt;br /&gt;
Datenregister &#039;&#039;&#039;TCNT1H&#039;&#039;&#039;/&#039;&#039;&#039;TCNT1L&#039;&#039;&#039; wird dabei als Auf-/Ab-Zähler&lt;br /&gt;
betrieben, welcher von 0 an aufwärts zählt bis zur Obergrenze und danach&lt;br /&gt;
wieder zurück auf 0.&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Obergrenze ergibt sich daraus, ob 8- 9- oder 10-Bit PWM verwendet wird und&lt;br /&gt;
zwar gemäss folgender Tabelle:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Auflösung&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;Obergrenze&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Frequenz&#039;&#039;&#039;&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;
Wenn nun der Zählerwert im Datenregister den in &#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;&lt;br /&gt;
gespeicherten Wert erreicht wird der Ausgabepin &#039;&#039;&#039;OC1&#039;&#039;&#039; gesetzt bzw.&lt;br /&gt;
gelöscht, je nach Einstellung von &#039;&#039;&#039;COM1A1&#039;&#039;&#039; und &#039;&#039;&#039;COM1A0&#039;&#039;&#039; im &#039;&#039;&#039;TCCR1A&#039;&#039;&#039;-Register.&lt;br /&gt;
&lt;br /&gt;
Ich habe versucht, die entsprechenden Signale in der folgenden Grafik&lt;br /&gt;
zusammenzufassen&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;
=== Vergleichswert-Überprüfung ===&lt;br /&gt;
&lt;br /&gt;
Hier wird in ein spezielles Vergleichswertregister (&#039;&#039;&#039;OCR1H&#039;&#039;&#039;/&#039;&#039;&#039;OCR1L&#039;&#039;&#039;)&lt;br /&gt;
ein Wert eingeschrieben, welcher ständig mit dem aktuellen Zählerwert&lt;br /&gt;
verglichen wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Erreicht der Zähler den in diesem Register eingetragenen Wert so kann ein&lt;br /&gt;
Signal (0 oder 1) am Pin &#039;&#039;&#039;OC1&#039;&#039;&#039; erzeugt und/oder ein Interrupt ausgelöst&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
=== Einfangen eines Eingangssignals (Input Capturing) ===&lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart wird an den Input Capturing Pin (ICP) des Controllers&lt;br /&gt;
eine Signalquelle angeschlossen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Nun kann je nach Konfiguration entweder ein Signalwechsel von 0 nach 1&lt;br /&gt;
(steigende Flanke) oder von 1 nach 0 (fallende Flanke) erkannt werden und der zu&lt;br /&gt;
diesem Zeitpunkt aktuelle Zählerstand in ein spezielles Register abgelegt&lt;br /&gt;
werden. Gleichzeitig kann auch ein entsprechender Interrupt ausgelöst werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn die Signalquelle ein starkes Rauschen beinhaltet kann die&lt;br /&gt;
Rauschunterdrückung eingeschaltet werden. Dann wird beim Erkennen der&lt;br /&gt;
konfigurierten Flanke über 4 Taktzyklen das Signal überwacht und nur dann,&lt;br /&gt;
wenn alle 4 Messungen gleich sind wird die entsprechende Aktion ausgelöst.&lt;br /&gt;
&lt;br /&gt;
== Gemeinsame Register ==&lt;br /&gt;
&lt;br /&gt;
Verschiedene Register beinhalten Zustände und Einstellungen, welche sowohl&lt;br /&gt;
für den 8-Bit, als auch für den 16-Bit Timer/Counter in ein und demselben&lt;br /&gt;
Register zu finden sind.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIMSK&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;M&#039;&#039;&#039;a&#039;&#039;&#039;sk&#039;&#039;&#039;&lt;br /&gt;
Register&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 1 ein Timer Overflow 1 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCIE1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare Match &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Beim Timer/Counter 1 kann zusätzlich zum Überlauf ein&lt;br /&gt;
Vergleichswert definiert werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird beim Erreichen des Vergleichswertes&lt;br /&gt;
ein Compare Match Interrupt ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TICIE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt&lt;br /&gt;
&#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Capture Event Interrupt&lt;br /&gt;
ausgelöst, wenn ein entsprechendes Signalereignis am Pin PD6(ICP)&lt;br /&gt;
auftritt. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein,&lt;br /&gt;
wenn auch ein entsprechender Interrupt ausgelöst werden soll.&amp;lt;font face=&amp;quot;Helvetica&amp;quot; size=&amp;quot;2&amp;quot;&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOIE0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird bei einem Überlauf des&lt;br /&gt;
Datenregisters des Timer/Counter 0 ein Timer Overflow 0 Interrupt&lt;br /&gt;
ausgelöst. Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
|- &lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TIFR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#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;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 1 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der PWM-Betriebsart wird das Bit gesetzt, wenn die Zählrichtung&lt;br /&gt;
von auf- zu abwärts und umgekehrt geändert wird (Zählerwert = 0).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;OCF1A&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;O&#039;&#039;&#039;utput &#039;&#039;&#039;C&#039;&#039;&#039;ompare &#039;&#039;&#039;F&#039;&#039;&#039;lag&lt;br /&gt;
Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn der aktuelle Wert des Datenregisters&lt;br /&gt;
von Timer/Counter 1 mit demjenigen im Vergleichsregister &#039;&#039;&#039;OCR1&#039;&#039;&#039;&lt;br /&gt;
übereinstimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ICF1&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;I&#039;&#039;&#039;nput &#039;&#039;&#039;C&#039;&#039;&#039;apture &#039;&#039;&#039;F&#039;&#039;&#039;lag Timer/Counter &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn ein Capture-Ereignis aufgetreten ist,&lt;br /&gt;
welches anzeigt, dass der Wert des Datenregisters des Timer/Counter&lt;br /&gt;
1 in das Input Capture Register ICR1 übertragen wurde.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;TOV0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;T&#039;&#039;&#039;imer/Counter &#039;&#039;&#039;O&#039;&#039;&#039;verflow Flag Timer/Counter&lt;br /&gt;
&#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird vom Controller gesetzt, wenn beim Timer 0 ein&lt;br /&gt;
Überlauf des Datenregisters stattfindet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn der zugehörige&lt;br /&gt;
Interrupt-Vektor aufgerufen wird. Es kann jedoch auch gelöscht&lt;br /&gt;
werden, indem eine logische 1 (!) in das entsprechende Bit&lt;br /&gt;
geschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
= Sleep-Modes =&lt;br /&gt;
&lt;br /&gt;
AVR Controller verfügen über eine Reihe von sogen. &#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-Comperators negativ beeinflussen. Der Controller wird durch Interrupts aus dem Schlaf geweckt. Welche Interrups den jeweiligen Schlafmodus beenden, ist einer Tabelle im Datenblatt des jeweiligen Controllers zu entnehmen.&lt;br /&gt;
Die Funktionen der avr-libc stehen nach Einbinden der header-Datei &#039;&#039;sleep.h&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| set_sleep_mode(uint8_t&amp;amp;nbsp;mode)&lt;br /&gt;
|Setzt den Schlafmodus, der bei Aufruf von sleep() aktiviert wird. In sleep.h sind einige Konstanten definiert (z.B.   SLEEP_MODE_PWR_DOWN). Die definierten Modi werden jedoch nicht alle von sämtlichten AVR-Controllern unterstützt.&lt;br /&gt;
|-&lt;br /&gt;
| sleep_mode()&lt;br /&gt;
| Versetzt den Controller in den mit set_sleep_mode gewählten Schlafmodus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
   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 &amp;quot;Aufwach-Interrupts&amp;quot; verarbeitet&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle AVR-Controller (v.a. nicht die &amp;quot;Brandneuen&amp;quot;) werden von den avr-libc sleep-Funktionen richtig angesteuert. Bei nicht-untersützten Typen erreicht man die gewollte Funktionalität durch direkte &amp;quot;Bitmanipulation&amp;quot; der ensprechenden Register (vgl. Datenblatt) und Aufruf des Sleep-Befehls via Inline-Assembler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 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;);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Power Management and Sleep-Modes&lt;br /&gt;
&lt;br /&gt;
= Der Watchdog =&lt;br /&gt;
&lt;br /&gt;
Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns&lt;br /&gt;
Programmierern, der Watchdog.&lt;br /&gt;
&lt;br /&gt;
So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut&lt;br /&gt;
perfekte und fehlerfreie Programm zu entwickeln.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er&lt;br /&gt;
kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in&#039;s Nirwana&lt;br /&gt;
verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers&lt;br /&gt;
ausgelöst wird.&lt;br /&gt;
&lt;br /&gt;
Betrachten wir doch einmal folgende Codesequenz:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;unsigned char x;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;x = 10;&#039;&#039;&#039;&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font face=&amp;quot;Courier New&amp;quot;&amp;gt;&#039;&#039;&#039;while (x &amp;gt;= 0) {&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; x--;&amp;lt;br /&amp;gt;&lt;br /&gt;
}&#039;&#039;&#039;&amp;lt;/font&amp;gt; --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t x;&lt;br /&gt;
&lt;br /&gt;
x = 10;&lt;br /&gt;
&lt;br /&gt;
while (x &amp;gt;= 0) {&lt;br /&gt;
   // tu was&amp;lt;br /&amp;gt;&lt;br /&gt;
   x--;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe&lt;br /&gt;
niemals beendet wird. Warum nicht? Ganz einfach, weil eine als &#039;&#039;&#039;&#039;&#039;unsigned&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
deklarierte Variable niemals kleiner als Null werden kann.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife&lt;br /&gt;
drehen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Und hier genau kommt der Watchdog zum Zug.&lt;br /&gt;
&lt;br /&gt;
== Wie funktioniert nun der Watchdog ==&lt;br /&gt;
&lt;br /&gt;
Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern&lt;br /&gt;
erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog&lt;br /&gt;
aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter&lt;br /&gt;
von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl&lt;br /&gt;
Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im&lt;br /&gt;
Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig&lt;br /&gt;
wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb&lt;br /&gt;
unserer Hauptschleife passieren.&lt;br /&gt;
&lt;br /&gt;
Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein&lt;br /&gt;
spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen&lt;br /&gt;
zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht&lt;br /&gt;
mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen&lt;br /&gt;
das Bit WDE auf 0 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Watchdog Control Register:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTCR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atchog &#039;&#039;&#039;T&#039;&#039;&#039;imer&amp;amp;nbsp; &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP2&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;WDP0&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDTOE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;T&#039;&#039;&#039;urn &#039;&#039;&#039;O&#039;&#039;&#039;ff &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, wenn das Bit &#039;&#039;&#039;WDE&#039;&#039;&#039; gelöscht&lt;br /&gt;
wird, andernfalls wird der Watchdog nicht ausgeschaltet.&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4&lt;br /&gt;
Taktzyklen automatisch wieder gelöscht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Bit kann nur gelöscht werden solange das Bit &#039;&#039;&#039;WDTOE&#039;&#039;&#039; auf 1&lt;br /&gt;
steht.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;WDP2&amp;lt;br /&amp;gt;&lt;br /&gt;
WDP1&amp;lt;br /&amp;gt;&lt;br /&gt;
WDP0&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;W&#039;&#039;&#039;atch&#039;&#039;&#039;d&#039;&#039;&#039;og Timer &#039;&#039;&#039;P&#039;&#039;&#039;rescaler Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog,&lt;br /&gt;
also, wie lange es dauert, bis ein Reset ausgelöst wird:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | WDP0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Anzahl Zyklen&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 3V&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | Typ. Timeoutzeit bei Vcc = 5V&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;
| align=&amp;quot;center&amp;quot; | 16K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 47ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 15ms&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;
| align=&amp;quot;center&amp;quot; | 32K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 94ms&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 30ms&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;
| align=&amp;quot;center&amp;quot; | 64K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.19s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 60ms&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;
| align=&amp;quot;center&amp;quot; | 128K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.38s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.12s&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;
| align=&amp;quot;center&amp;quot; | 256K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.75s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.24s&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;
| align=&amp;quot;center&amp;quot; | 512K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.5s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.49s&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;
| align=&amp;quot;center&amp;quot; | 1024K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0.97s&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;
| align=&amp;quot;center&amp;quot; | 2048K&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6s&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1.9s&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei&lt;br /&gt;
wdt.h in die Quelldatei eingebunden werden. &lt;br /&gt;
&amp;lt;!-- mt: das stimmt wohl nicht mehr?!:&lt;br /&gt;
Dadurch wird auch der Startup-Code&lt;br /&gt;
entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch&lt;br /&gt;
gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls&lt;br /&gt;
ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen&lt;br /&gt;
eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt&lt;br /&gt;
werden:&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; --defsym __init_wdtcr__=0x1f&amp;lt;br /&amp;gt;&lt;br /&gt;
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.&amp;lt;br /&amp;gt; --&amp;gt;&lt;br /&gt;
Danach können die folgenden Funktionen verwendet werden:&lt;br /&gt;
&lt;br /&gt;
{| border=1&lt;br /&gt;
| wdt_enable(uint8_t&amp;amp;nbsp;timeout)&lt;br /&gt;
|Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen. Einige Timeout-Werte sind als Konstanten vordefiniert (z.B. WDTO_30MS).&lt;br /&gt;
|-&lt;br /&gt;
| wdt_disable()&lt;br /&gt;
| Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.&lt;br /&gt;
|-&lt;br /&gt;
| wdt_reset()&lt;br /&gt;
| Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht den AVR zurücksetzt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann das &#039;&#039;&#039;WDTCR&#039;&#039;&#039;-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: (TODO link to avr-libc/modules/watchdog)&lt;br /&gt;
&lt;br /&gt;
== Ein paar grundsätzliche Gedanken ==&lt;br /&gt;
&lt;br /&gt;
Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine&lt;br /&gt;
philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.&lt;br /&gt;
&lt;br /&gt;
Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm&lt;br /&gt;
etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer&lt;br /&gt;
wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über&lt;br /&gt;
Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin&lt;br /&gt;
liegt.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/Watchdog timer handling&lt;br /&gt;
&lt;br /&gt;
= Programmieren mit Interrupts =&lt;br /&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;
Als erstes wollen wir uns noch einmal den allgemeinen Programmablauf bei der&lt;br /&gt;
Interrupt-Programmierung zu Gemüte führen.&lt;br /&gt;
&lt;br /&gt;
[[Image:Interrupt Programme.gif]]&lt;br /&gt;
&lt;br /&gt;
Man sieht, dass die Interruptroutine quasi parallel zum Hauptprogramm&lt;br /&gt;
abläuft. Da wir nur eine CPU haben ist es natürlich keine echte Parallelität,&lt;br /&gt;
sondern das Hauptprogramm wird beim Eintreffen eines Interrupts unterbrochen,&lt;br /&gt;
die Interruptroutine wird ausgeführt und danach erst wieder zum Hauptprogramm&lt;br /&gt;
zurückgekehrt.&lt;br /&gt;
&lt;br /&gt;
== Anforderungen an die Interrupt-Routine ==&lt;br /&gt;
&lt;br /&gt;
Um unliebsamen Überraschungen vorzubeugen sollten einige Grundregeln bei&lt;br /&gt;
der Gestaltung der Interruptroutinen beachtet werden.&lt;br /&gt;
&lt;br /&gt;
* Die Interruptroutine soll möglichst kurz und schnell abarbeitbar sein, daraus folgt:&lt;br /&gt;
* Keine unfangreichen Berechnungen innerhalb der Interruptroutine.&lt;br /&gt;
* Keine endlos langen Programmschleifen.&lt;br /&gt;
* Obschon es möglich ist, während der Abarbeitung einer Interruptroutine andere oder sogar den gleichen Interrupt wieder zuzulassen rate ich dringend von solchen Spielen ab.&lt;br /&gt;
&lt;br /&gt;
== Interrupt-Quellen ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Ereignisse können einen Interrupt auf dem AVR auslösen, wobei&lt;br /&gt;
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;
== Register ==&lt;br /&gt;
&lt;br /&gt;
Der AT90S2313 verfügt über 2 Register welche mit den&lt;br /&gt;
Interrupts zusammen hängen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&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;ask &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT1&#039;&#039;&#039;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;1&#039;&#039;&#039; Enable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Interrupt&lt;br /&gt;
ausgelöst wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine steigende oder fallende (je&lt;br /&gt;
nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang&lt;br /&gt;
geschaltet ist. Auf diese Weise bietet sich die Möglichkeit,&lt;br /&gt;
Software-Interrupts zu realisieren.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INT0&#039;&#039;&#039;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Request &#039;&#039;&#039;0&#039;&#039;&#039; Enable&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn dieses Bit gesetzt ist wird ein Interrupt&lt;br /&gt;
ausgelöst wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine steigende oder fallende (je&lt;br /&gt;
nach Konfiguration im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;) Flanke erkannt wird.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Global&lt;br /&gt;
Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&amp;lt;br /&amp;gt;&lt;br /&gt;
Der Interrupt wird auch ausgelöst, wenn der Pin als Ausgang&lt;br /&gt;
geschaltet ist. Auf diese Weise bietet sich die Möglichkeit,&lt;br /&gt;
Software-Interrupts zu realisieren.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF1&#039;&#039;&#039;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;1&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin eine&lt;br /&gt;
Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird.&lt;br /&gt;
Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine&lt;br /&gt;
beendet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039;&lt;br /&gt;
eingeschrieben wird.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;INTF0&#039;&#039;&#039;&lt;br /&gt;
| External &#039;&#039;&#039;Int&#039;&#039;&#039;errupt Flag &#039;&#039;&#039;0&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit wird gesetzt, wenn am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin eine&lt;br /&gt;
Interrupt-Kondition, entsprechend der Konfiguration, erkannt wird.&lt;br /&gt;
Wenn das Global Enable Interrupt Flag gesetzt ist wird die&lt;br /&gt;
Interruptroutine angesprungen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Flag wird automatisch gelöscht, wenn die Interruptroutine&lt;br /&gt;
beendet ist.&amp;lt;br /&amp;gt;&lt;br /&gt;
Alternativ kann das Flag gelöscht werden, indem der Wert &#039;&#039;&#039;1(!)&#039;&#039;&#039;&lt;br /&gt;
eingeschrieben wird.&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das MCU Control Register enthält Kontrollbits für allgemeine&lt;br /&gt;
MCU-Funktionen.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Bit&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 7&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 6&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 5&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 4&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 3&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 2&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 1&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;-&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;R/W&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R/W&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | R&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;Initialwert&#039;&#039;&#039;&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;
| 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;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SE&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;E&#039;&#039;&#039;nable&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit muss gesetzt sein, um den Controller mit dem &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehl&lt;br /&gt;
in den Schlafzustand versetzen zu können.&amp;lt;br /&amp;gt;&lt;br /&gt;
Um den Schlafmodus nicht irrtümlich einzuschalten wird empfohlen,&lt;br /&gt;
das Bit erst unmittelbar vor Ausführung des &#039;&#039;&#039;SLEEP&#039;&#039;&#039;-Befehls zu&lt;br /&gt;
setzen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;SM&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;S&#039;&#039;&#039;leep &#039;&#039;&#039;M&#039;&#039;&#039;ode&amp;lt;br /&amp;gt;&lt;br /&gt;
Dieses Bit bestimmt der Schlafmodus.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ist das Bit gelöscht so wird der &#039;&#039;&#039;Idle&#039;&#039;&#039;-Modus ausgeführt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ist das Bit gesetzt so wird der &#039;&#039;&#039;Power-Down&#039;&#039;&#039;-Modus ausgeführt.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ISC11&amp;lt;br /&amp;gt;&lt;br /&gt;
ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#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;&lt;br /&gt;
Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese beiden Bits bestimmen, ob die steigende oder die fallende&lt;br /&gt;
Flanke für die Interrupterkennung am &#039;&#039;&#039;INT1&#039;&#039;&#039;-Pin ausgewertet&lt;br /&gt;
wird.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC11&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC10&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert,&lt;br /&gt;
solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank an &#039;&#039;&#039;INT1&#039;&#039;&#039; erzeugt einen&lt;br /&gt;
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&lt;br /&gt;
einen Interrupt.&lt;br /&gt;
|}&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;ISC01&amp;lt;br /&amp;gt;&lt;br /&gt;
ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#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;&lt;br /&gt;
Bits&amp;lt;br /&amp;gt;&lt;br /&gt;
Diese beiden Bits bestimmen, ob die steigende oder die fallende&lt;br /&gt;
Flanke für die Interrupterkennung am &#039;&#039;&#039;INT0&#039;&#039;&#039;-Pin ausgewertet&lt;br /&gt;
wird.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC01&#039;&#039;&#039;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; | &#039;&#039;&#039;ISC00&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;Bedeutung&#039;&#039;&#039;&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.&amp;lt;br /&amp;gt;&lt;br /&gt;
In der Beschreibung heisst es, der Interrupt wird getriggert,&lt;br /&gt;
solange der Pin auf 0 bleibt, also eigentlich unbrauchbar.&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 Flank an &#039;&#039;&#039;INT0&#039;&#039;&#039; erzeugt einen&lt;br /&gt;
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&lt;br /&gt;
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&lt;br /&gt;
Interrupt Enable&#039;&#039;&#039; Bit im Status Register &#039;&#039;&#039;SREG&#039;&#039;&#039; gelöscht und alle&lt;br /&gt;
weiteren Interrupts unterbunden. Obwohl es möglich ist, zu diesem&lt;br /&gt;
Zeitpunkt bereits wieder das GIE-bit zu setzen rate ich dringend davon ab. Dieses&lt;br /&gt;
wird nämlich automatisch gesetzt, wenn die Interruptroutine beendet wird. Wenn&lt;br /&gt;
in der Zwischenzeit weitere Interrupts eintreffen werden die zugehörigen&lt;br /&gt;
Interrupt-Bits gesetzt und die Interrupts bei Beendigung der laufenden&lt;br /&gt;
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&lt;br /&gt;
ständig und in kurzer Folge auftritt. Dieser sperrt dann möglicherweise alle&lt;br /&gt;
anderen Interrupts mit niedrigerer Priorität. Dies ist einer der Gründe,&lt;br /&gt;
weshalb die Interrupt-Routinen sehr kurz gehalten werden sollen.&lt;br /&gt;
&lt;br /&gt;
=== Das Status-Register ===&lt;br /&gt;
&lt;br /&gt;
Es gilt auch zu beachten, dass das Status-Register während der Abarbeitung&lt;br /&gt;
einer Interruptroutine nicht automatisch gesichert wird. Falls notwendig muss dies vom&lt;br /&gt;
Programmierer selber vorgesehen werden.&lt;br /&gt;
&lt;br /&gt;
== Interrupts mit dem AVR GCC Compiler (WinAVR) ==&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich können alle Interruptspezifischen Registerzugriffe wie&lt;br /&gt;
gewohnt über I/O-Adressierung vorgenommen werden. Etwas einfacher geht es&lt;br /&gt;
jedoch, wenn wir die vom Compiler zur Verfügung gestellten Mittel einsetzen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Damit diese Mittel zur Verfügung stehen müssen wir die Includedatei&lt;br /&gt;
interrupt.h einbinden mittels&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und dann kann&#039;s losgehen&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;sei()&#039;&#039;&#039; schaltet die Interrupts ein. Eigentlich wird&lt;br /&gt;
nichts anderes gemacht als das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status&lt;br /&gt;
Register gesetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    sei();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Makro &#039;&#039;&#039;cli()&#039;&#039;&#039; schaltet die Interrupts aus oder anders&lt;br /&gt;
gesagt, das &#039;&#039;&#039;Global Interrupt Enable&#039;&#039;&#039; Bit im Status Register wird&lt;br /&gt;
gelöscht.&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    cli();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oft steht man vor der Aufgabe, dass eine Codesequenz nicht unterbrochen werden darf.&lt;br /&gt;
Es liegt dann nahe, zu Beginn dieser Sequenz ein cli() und am Ende ein sei() einzufügen.&lt;br /&gt;
Dies ist jedoch ungünstig, wenn die Interrupts vor Aufruf der Sequenz deaktiviert waren&lt;br /&gt;
und danach auch weiterhin deaktiviert bleiben sollen. Ein sei() würde ungeachtet des vorherigen &lt;br /&gt;
Zustands die Interrups aktivieren, was zu unerwünschten Seiteneffekten führen kann.&lt;br /&gt;
Die aus dem folgenden Beispiel ersichtliche Vorgehensweise ist in solchen Fällen vorzuziehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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 nutzbar machen&lt;br /&gt;
     ohne die JTAG-Fuse-Bit zu aendern */&lt;br /&gt;
&lt;br /&gt;
   MCUCR |= (1&amp;lt;&amp;lt;JTD);&lt;br /&gt;
   MCUCR |= (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;
   NichSoGut();&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;/pre&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;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem nun die Interrupts aktiviert sind braucht es selbstverständlich noch&lt;br /&gt;
den auszuführenden Code, der ablaufen soll wenn ein Interrupt eintrifft.&amp;lt;br /&amp;gt;&lt;br /&gt;
Dazu gibt es zwei Definitionen welche allerdings AVR-GCC spezifisch sind und&lt;br /&gt;
bei anderen Compilern womöglich anders heissen können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
SIGNAL (siglabel)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit SIGNAL wird eine Funktion für die Bearbeitung eines Interrupts&lt;br /&gt;
eingeleitet. Als Argument muss dabei die Benennung des entsprechenden&lt;br /&gt;
Interruptvektoren angegeben werden. Diese sind in den jeweiligen Includedateien&lt;br /&gt;
IOxxxx.h zu finden. Mögliche Funktionsrümpfe für solche Interruptfunktionen&lt;br /&gt;
sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_INTERRUPT0)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
SIGNAL (SIG_OVERFLOW1)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Während der Ausführung des Funktion sind alle weiteren Interrupts&lt;br /&gt;
automatisch gesperrt. Beim Verlassen der Funktion werden die Interrupts wieder&lt;br /&gt;
zugelassen.&lt;br /&gt;
&lt;br /&gt;
Sollte während der Abarbeitung der Interruptroutine ein weiterer Interrupt&lt;br /&gt;
(gleiche oder andere Interruptquelle) auftreten so wird das entsprechende Bit im&lt;br /&gt;
zugeordneten Interrupt Flag Register gesetzt und die entsprechende&lt;br /&gt;
Interruptroutine automatisch nach dem Beenden der aktuellen Funktion aufgerufen.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Problem ergibt sich eigentlich nur dann, wenn während der Abarbeitung der&lt;br /&gt;
aktuellen Interruptroutine mehrere gleichartige Interrupts auftreten. Die&lt;br /&gt;
entsprechende Interruptroutine wird im Nachhinein zwar aufgerufen jedoch wissen&lt;br /&gt;
wir nicht, ob nun der entsprechende Interrupt einmal, zweimal oder gar noch&lt;br /&gt;
öfter aufgetreten ist. Deshalb soll hier noch einmal betont werden, dass&lt;br /&gt;
Interruptroutinen so schnell wie nur irgend möglich wieder verlassen werden&lt;br /&gt;
sollten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
INTERRUPT (signame)&lt;br /&gt;
{&lt;br /&gt;
    /* Interrupt Code */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit INTERRUPT wird genau gleich gearbeitet wie mit SIGNAL. Der Unterschied&lt;br /&gt;
ist derjenige, dass bei INTERRUPT beim Aufrufen der Funktion das &#039;&#039;&#039;Global&lt;br /&gt;
Enable Interrupt&#039;&#039;&#039; Bit automatisch wieder gesetzt und somit weitere Interrupts&lt;br /&gt;
zugelassen werden. Dies kann zu nicht unerheblichen Problemen von im einfachsten&lt;br /&gt;
Fall einem Stack overflow bis zu sonstigen unerwarteten Effekten führen und&lt;br /&gt;
sollte wirklich nur dann angewendet werden wenn man sich absolut sicher ist, das&lt;br /&gt;
Ganze auch im Griff zu haben.&lt;br /&gt;
&lt;br /&gt;
siehe auch: Hinweise in [[AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
== Datenaustausch mit Interrupt-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Variablen auf 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 wird und nach jedem Schreibzugriff in den Speicher geschrieben wird. Ansonsten könnte die Code-Optimierung &amp;quot;greifen&amp;quot; und der Wert der Variablen nur in Prozessorregistern zwischenspeichert werden, die &amp;quot;nichts von der Änderung woanders mitbekommen&amp;quot;.&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;pre class=&amp;quot;code&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;avr/signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.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.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 z.B. alle 10ms&lt;br /&gt;
SIGNAL(SIG_OUTPUT_COMPARE1A)&lt;br /&gt;
{&lt;br /&gt;
   // hier wird gKeyCounter veraendert, die uebrigen&lt;br /&gt;
   // Programmteile muessen 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 (gKeyCouter &amp;lt; CNTREPEAT) gKeyCounter++;&lt;br /&gt;
   else gKeyCounter = 0;&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 geschrieben 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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei Variablen größer ein Byte muss darauf geachtet werden, dass die Zugriffe auf die einzelnen Bytes ausserhalb 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
volatile uint16_t gMyCounter16bit&lt;br /&gt;
...&lt;br /&gt;
SIGNAL(...)&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 Kopieren des zweiten Bytes &lt;br /&gt;
   // ein Interrupt auftritt der den Inhalt von MyCounter veraendert&lt;br /&gt;
   tmpCnt = gMyCounter16bit; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   // besser: Aenderungen &amp;quot;ausserhalb&amp;quot; verhindern, alle &amp;quot;Teilbytes&amp;quot;&lt;br /&gt;
   // bleiben konsistent&lt;br /&gt;
   cli();  // Interupts deaktivieren&lt;br /&gt;
   tmpCnt = gMyCounter16Bit;&lt;br /&gt;
   sei();  // wieder aktivieren&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
== Was macht das Hauptprogramm ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall gar nichts mehr.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist also durchaus denkbar, ein Programm zu schreiben, welches in der&lt;br /&gt;
main-Funktion lediglich noch die Interrupts aktiviert und dann in eine&lt;br /&gt;
Endlosschleife folgender Art verzweigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    for (;;) {}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird man allerdings in den Interruptroutinen die Interrupts&lt;br /&gt;
erfassen und im Hauptprogramm dann gemütlich auswerten.&lt;br /&gt;
&lt;br /&gt;
Wie wir im bisherigen Kursverlauf gesehen haben ist es ohnehin mit so&lt;br /&gt;
schnellen Controller meistens gar nicht unbedingt notwendig mit&lt;br /&gt;
Interruptfunktionen zu arbeiten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Es ist allerdings auch zu bemerken, dass mit den Interruptroutinen ein Programm&lt;br /&gt;
sehr schön strukturiert werden kann, wenn man es richtig macht.&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;
= 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 (eigentlich [[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.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 wenig Sinn macht und auch nur bei einigen RAM-losen Typen nach &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.&lt;br /&gt;
&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;
== Programmspeicher (Flash) ==&lt;br /&gt;
&lt;br /&gt;
Ein Zugriff auf Konstanten im Programmspeicher ist mittels avr-gcc nicht &amp;quot;transparent&amp;quot; möglich. D.h. es sind besondere Zugriffsfunktionen erforderlich um Daten aus diesem Speicher zu lesen. Grundsätzlich basieren alle Zugriffsfunktionen auf der Assembler-Anweisung lpm (load program memory). Die Standard-Laufzeitbibliothek des avr-gcc, die [[avr-libc]], stellt diese Funktionen nach einbinden der Header-Datei pgmspace.h zur Verfügung. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Deklarationen von Variablen im Flash-Speicher werden durch das &amp;quot;Attribut&amp;quot; PROGMEM ergänzt. Einige Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
const uint8_t pgmFooByte PROGMEM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
const uint16_t pgmFooWort PROGMEM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
const uint8_t pgmFooByteArray1[] PROGMEM = { 18, 3 ,70 };&lt;br /&gt;
const uint8_t pgmFooByteArray2[] PROGMEM = { 30, 7 ,79 };&lt;br /&gt;
&lt;br /&gt;
/* Zeiger */&lt;br /&gt;
const uint8_t *pgmPointerToArray1 PROGMEM = pgmFooByteArray1;&lt;br /&gt;
const uint8_t *pgmPointerArray[] PROGMEM = { pgmFooByteArray1, pgmFooByteArray1 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Byte lesen ===&lt;br /&gt;
&lt;br /&gt;
Mit der Funktion pgm_read_byte aus pgmspace.h erfolgt der Zugriff auf die Daten. Parameter der Funktion ist die Adresse des Bytes im Flash-Speicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    // Wert der Ram-Variablen myByte auf den Wert von pgmFooByte setzen:&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
&lt;br /&gt;
    myByte = pgm_read_byte(&amp;amp;pgmFooByte);&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    // Schleife ueber ein Array aus Byte-Werten im Flash&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(&amp;amp;pgmFooByteArray1[i]);&lt;br /&gt;
        // mach&#039; was mit myByte...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wort lesen ===&lt;br /&gt;
&lt;br /&gt;
Für &amp;quot;einfache&amp;quot; 16-bit breite Variablen erfolgt der Zugriff analog zum Byte-Beispiel jedoch mit der Funktion pgm_read_word.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = pgm_read_word(&amp;amp;pgmFooWord);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeiger auf Werte im Flash sind ebenfalls 16 Bits &amp;quot;gross&amp;quot;. (Die avr-libc pgmspace-Funktionen unterstützen nur die unteren 64kB Flash bei Controllern mit mehr als 64kB.) Pointer müssen gegebenenfalls &amp;quot;gecastet&amp;quot; werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
    uint8_t *ptrToArray;&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerToArray1));&lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray1&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (18, 3, 70)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    ptrToArray = (uint8_t*)(pgm_read_word(&amp;amp;pgmPointerArray[1]));&lt;br /&gt;
    &lt;br /&gt;
    // ptrToArray zeigt nun auf das erste Element des Byte-Arrays pgmPointerToArray2&lt;br /&gt;
    // da im zweiteb Element des Pointer-Arrays pgmPointerArray die Adresse&lt;br /&gt;
    // von pgmPointerToArray2 abgelegt ist&lt;br /&gt;
&lt;br /&gt;
    for (i=0;i&amp;lt;3;i++) {&lt;br /&gt;
        myByte = pgm_read_byte(ptrToArray+i);&lt;br /&gt;
        // mach&#039; was mit myByte... (30, 7, 79)&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Datenblock lesen ===&lt;br /&gt;
&lt;br /&gt;
TODO: Beispiele, structs, strings, union-&amp;quot;trick&amp;quot; fuer floats&lt;br /&gt;
&lt;br /&gt;
=== Vereinfachung für Zeichenketten (Strings) im Flash ===&lt;br /&gt;
&lt;br /&gt;
Zeichenketten können innerhalb des Quellcodes als &amp;quot;Flash-Variablen&amp;quot; ausgewiesen werden. Dazu dient das Makro PSTR aus pgmspace.h. Dies erspart die getrennte Deklaration mit PROGMEM-Attribut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXLEN 30&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
char StringImRam[MAXLEN];&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    strcpy(StringImRam, &amp;quot;Mueller-Luedenscheidt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, StringImFlash, 5) { &lt;br /&gt;
        // mach&#039; was, wenn die ersten 5 Zeichen identisch - hier nicht&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // der Code hier wuerde ausgefuehrt &lt;br /&gt;
    } &lt;br /&gt;
&lt;br /&gt;
    if (!strncmp_P(StringImRam, PSTR(&amp;quot;Mueller-Schmitt&amp;quot;), 5)) {&lt;br /&gt;
        // der Code hier wuerde ausgefuerht, die ersten 5 Zeichen stimmen ueberein&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        // wuerde bei nicht-Uebereinstimmung ausgefuehrt&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Übergibt man Zeichenketten (genauer: die Adresse des ersten Zeichens), die im Flash abglegt sind an eine Funktion, muss diese entsprechend programmiert sein. Die Funktion selbst hat keine Möglichkeit zu unterscheiden ob es sich um eine Adresse im Flash  oder im RAM handelt. Die avr-libc und viele andere avr-gcc-Bibliotheken halten sich an die Konvention, dass Namen von Funktionen die Flash-Adressen erwarten mit dem Suffix _p (oder _P) versehen sind. &lt;br /&gt;
&lt;br /&gt;
Von einigen Bibliotheken werden Makros definiert, die &amp;quot;automatisch&amp;quot; ein PSTR bei Verwendung einer Funktion einfügen. Ein Blick in den Header-File der Bibliothek zeigt, ob dies der Fall ist. Ein Beispiel aus P. Fleurys lcd-Library:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
// Ausschnitt aus dem Header-File lcd.h der &amp;quot;Fleury-LCD-Lib.&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
extern void lcd_puts_p(const char *progmem_s);&lt;br /&gt;
#define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
// in einer Anwendung (wieauchimmmer.c)&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;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
char StringImFlash[] PROGMEM = &amp;quot;Erwin Lindemann&amp;quot;; // im &amp;quot;Flash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
    lcd_puts_p(StringImFlash); &lt;br /&gt;
    lcd_puts_P(&amp;quot;Dr. Kloebner&amp;quot;); &lt;br /&gt;
    // daraus wird wg. #define lcd_put_P...:  lcd_puts_p( PSTR(&amp;quot;Dr. Kloebner&amp;quot;) );&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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 auch vom Anwendungsprogramm selbst beschrieben werden. Dies ist nur möglich, wenn die Schreibfunktionen in einem besonderen Speicherbereich (boot-section) des Programmspeichers/Flash abgelegt sind. 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;
=== Warum so kompliziert? ===&lt;br /&gt;
&lt;br /&gt;
Zu dem Thema, warum die Verabeitung von Werten aus dem Flash-Speicher so &amp;quot;kompliziert&amp;quot; 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 und der gcc-Compiler sehen keine unterschiedlichen Adressräume vor. &lt;br /&gt;
Hat man zum Beispiel eine Funktion string_an_uart(const char* s) und übergibt an diese Funktion die Adresse einer Zeichenkette (einen Pointer, z.B. 0x01ff), &amp;quot;weiss&amp;quot; die Funktion nicht, ob die Adresse auf den Flash-Speicher oder den/das RAM zeigt. Allein aus dem Pointer-Wert (der Zahl) kann nicht geschlossen werden, ob ein &amp;quot;einfaches&amp;quot; zeichen_an_uart(s[i]) oder zeichen_an_uart(pgm_read_byte(&amp;amp;s[i]) genutzt werden muss, um das i-te Zeichen auszugeben.&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Compiler &amp;quot;tricksen&amp;quot; etwas, in dem sie für einen Pointer nicht nur die Adresse anlegen, sondern zusätzlich noch den Ablageort (Flash oder RAM) sichern. Dies hat jedoch nicht nur Vorteile; Erläuterungen warum dies so ist, führen an dieser Stelle zu weit.&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [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;
== EEPROM ==&lt;br /&gt;
&lt;br /&gt;
Man beachte, das 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. &lt;br /&gt;
Dies gilt für jede einzelne Zelle. Bei geschickter Programmierung (z.B. Ring-Puffer), bei der die zu beschreibenden Zellen regelmässig gewechselt werden, kann man eine deutlich höhere Anzahl an Schreibzugriffen, bezogen auf den Gesamtspeicher, erreichen.&lt;br /&gt;
&lt;br /&gt;
Schreib- und Lesezugriffe auf den EEPROM-Speicher erfolgen über die im Modul eeprom.h definierten Funktionen. Mit diesen Funktionen können einzelne Bytes, Datenworte (16bit) und Datenblöcke geschrieben und gelesen werden. &lt;br /&gt;
&lt;br /&gt;
Bei Nutzung des EEPROMs ist zu beachten, das vor dem Zugriff auf diesen Speicher abgefragt wird, ob der Controller die vorherige EEPROM-Operation abgeschlossen hat. Die avr-libc-Funktionen beinhalten diese Prüfung, man muss sie nicht selbst implementieren. Man sollte auch verhindern, dass der Zugriff durch die Abarbeitung einer Interrupt-Routine unterbrochen wird, da bestimme Befehlsabfolgen vorgeben sind (&amp;quot;timed sequence&amp;quot;). Diese Prüfung wird nicht implizit durchgeführt. Im Zweifel kann man Sicherheitshalber bei EEPROM-Zugriffen die Interrupts global deaktivieren, z.B. so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
/* hier die eeprom-Zugriffe */&lt;br /&gt;
&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Deklaration einer Variable im EEPROM, ist das Attribut für die Section &amp;quot;.eeprom&amp;quot; zu ergänzen. Die Nutzung einer Präprozessor-Ersetzung bringt etwas Bequemlichkeit. Siehe dazu folgendes Beispiel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.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;
// alle Textstellen EEPROM im Quellcode durch __attribute__ ... ersetzen&lt;br /&gt;
#define EEPROM  __attribute__ ((section (&amp;quot;.eeprom&amp;quot;)))&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
/* Byte */&lt;br /&gt;
uint8_t eeFooByte EEPROM = 123;&lt;br /&gt;
&lt;br /&gt;
/* Wort */&lt;br /&gt;
uint16_t eeFooWort EEPROM = 12345;&lt;br /&gt;
&lt;br /&gt;
/* Byte-Feld */&lt;br /&gt;
uint8_t eeFooByteArray1[] EEPROM = { 18, 3 ,70 };&lt;br /&gt;
uint8_t eeFooByteArray2[] EEPROM = { 30, 7 ,79 };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: read_block erläutern &amp;quot;union-trick&amp;quot;, structs etc.&lt;br /&gt;
&lt;br /&gt;
=== Bytes lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
Die avr-libc Funktion zum Lesen eines Bytes heisst 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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint8_t myByte;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    sreg = SREG; // (1)&lt;br /&gt;
    cli();       // (1)&lt;br /&gt;
&lt;br /&gt;
    myByte = eeprom_read_byte(&amp;amp;eeFooByte); // lesen&lt;br /&gt;
    // myByte hat nun den Wert 123&lt;br /&gt;
...&lt;br /&gt;
    myByte = 99;&lt;br /&gt;
    eeprom_write_byte(&amp;amp;eeFooByte, myByte); // schreiben&lt;br /&gt;
...&lt;br /&gt;
    SREG = sreg; // (1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die mit (1) markierten Zeilen dienen hierbei dem (möglicherweise übertriebenen) Schutz vor Unterbrechnungen durch Interrupts.&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;pre class=&amp;quot;code&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    uint16_t myWord;&lt;br /&gt;
&lt;br /&gt;
    myWord = eeprom_read_word(&amp;amp;eeFooWord); // lesen&lt;br /&gt;
    // myWord hat nun den Wert 12345&lt;br /&gt;
...&lt;br /&gt;
    myWord = 2222;&lt;br /&gt;
    eeprom_write_word(&amp;amp;eeFooWord, myWord); // schreiben&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Block lesen/schreiben ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== EEPROM-Speicherabbild in .eep-Datei ===&lt;br /&gt;
&lt;br /&gt;
Eine besondere Funktion des avr-gcc ist, dass mit einem entsprechenden makefile aus den Initialisierungswerten der Variablen im Quellcode eine Datei erzeugt werden kann, die man auf den Controller programmieren kann (.eep-Datei). Damit können sehr elegant Standardwerte für den EEPROM-Inhalt im Quellcode definiert werden. Die Vorgehensweise wird aus dem [[WinAVR]]-Beispielmakefile ersichtlich.&lt;br /&gt;
&lt;br /&gt;
=== Bekannte Probleme bei den EEPROM-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Vorsicht: Nicht alle neuen AVR Controller werden von avr-libc/eeprom.h untersützt (Stand Version 1.0.4). Insbesondere beim ATMega169 funktionieren die Funktionen nicht korrekt (Ursache: unterschiedliche Speicheradressen der EEPROM-Register) Etwas ältere Typen, oder zu den &amp;quot;etablierten&amp;quot; Controllern kompatible, bereiten jedoch hier keine Proleme. Im Zweifel hilft ein Blick in den vom Compiler erzeugten Assembler-Code (lst/lss-Dateien).&lt;br /&gt;
&lt;br /&gt;
* siehe auch: [http://www.nongnu.org/avr-libc/user-manual/index.html Dokumentation der avr-libc] Abschnitt Modules/EEPROM handling&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Externe Baugrupen =&lt;br /&gt;
==I2C==&lt;br /&gt;
===Master===&lt;br /&gt;
[http://homepage.sunrise.ch/mysunrise/pfleury/group__pfleury__ic2master.html I2C Master library]&lt;br /&gt;
===Slave===&lt;br /&gt;
==CAN==&lt;br /&gt;
&lt;br /&gt;
==LCD==&lt;br /&gt;
=== HD44870===&lt;br /&gt;
[http://homepage.sunrise.ch/mysunrise/pfleury/group__pfleury__lcd.html LCD]&lt;br /&gt;
=== Nokia3310===&lt;br /&gt;
=== Nokia 6100 LCD===&lt;br /&gt;
Das Nokia ist ein 132x132x4096 Farben Display das bei eBay ziemlich preiswert ersteigert werden kann.&lt;br /&gt;
[http://www.apetech.de/nokia6100.php Nokia 6100 LCD Library]&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Reichelt-Wishlist&amp;diff=4339</id>
		<title>Reichelt-Wishlist</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Reichelt-Wishlist&amp;diff=4339"/>
		<updated>2004-09-29T14:24:22Z</updated>

		<summary type="html">&lt;p&gt;Oxygene: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Diese Seite soll das Produkt von folgendem Thread aus dem Forum werden:&lt;br /&gt;
http://www.mikrocontroller.net/forum/read-1-107307.html&lt;br /&gt;
&lt;br /&gt;
Ziel ist es, eine &amp;quot;Top-10&amp;quot; (oder mehr ?) Liste mit Artikeln zu erstellen, &lt;br /&gt;
die Reichelt in sein Programm aufnehmen sollte. Damit sich die beliebtesten Artikel herauskristalisieren, macht jeder einfach einen virtuellen Strich dahinter: | (ALT-GR Taste und &amp;lt; Taste drücken)&lt;br /&gt;
&lt;br /&gt;
Alle fünf Striche (|||||) bitte immer ein Leerzeichen einfügen.&lt;br /&gt;
Neue Artikel einfügen darf und soll natürlich auch jeder - aber bitte die Liste vorher durchgehen (Tipp: Browser-Suchfunktion nutzen)! Einfach ganz viele Striche auf einmal, hinter einem Artikel, einzufügen ist zwecklos. Das erkennt man in der History und es gibt viele Leute, die diese Seite überwachen...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nicht sinvoll ist etwas sehr exotisches&#039;&#039;&#039;, wie z.B. einen ganz bestimmten, super schnellen, AD-Wandler hier aufzulisten! Neue Artikel müssen sich für Reichelt ja auch rentieren und wirtschaftlich &amp;quot;an den Mann bringbar&amp;quot; sein.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* FTDI USB Chips  ||||| ||||| ||||| ||||| |||||&lt;br /&gt;
* Stift-/Buchsenleisten zum Auseinanderbrechen  ||||| ||||| ||||| ||||| |&lt;br /&gt;
* Drehimpulsgeber (konkreter Vorschlag von O.R.: PEC16-4220F-S0024 von Bourns)  ||||| ||||| ||||| |||||&lt;br /&gt;
* MMC / SDC slot  ||||| ||||| |||||&lt;br /&gt;
* VLSI MP3 Decoder  ||||| ||||| |||||&lt;br /&gt;
* dünner Schaltdraht (&amp;lt; 1mm Durchmesser, isoliert mit Tefzel oder Kynar)  ||||| ||||| ||&lt;br /&gt;
* DCF77 Empfangsmodule  ||||| ||||&lt;br /&gt;
* mehr SMD Bauteile  ||||| ||||| ||&lt;br /&gt;
* mehr und v.a. kleine (Hand-) Gehäuse  ||||| |||&lt;br /&gt;
* CAN-Bus Controller MCP2515  |||||&lt;br /&gt;
* Buchsenleisten zum Crimpen (allseitig anreihbar!, 1x1, 1x2)  |||&lt;br /&gt;
* Eisen III Chlorid  ||||| ||&lt;br /&gt;
* Shunt-Widerstände  |||&lt;br /&gt;
* T215 ersetzen gegen etwas Qualitativeres  ||&lt;br /&gt;
* Print-Steckverbinder (die einreihigen Stecker auf dem PC-Mainboard) ||||&lt;br /&gt;
* CompactFlash Stecker ||||&lt;br /&gt;
* Sensirion SHT11 |&lt;br /&gt;
&lt;br /&gt;
==== Bereits im Sortiment ====&lt;br /&gt;
Evtl. genauer spezifizieren...&lt;br /&gt;
&lt;br /&gt;
* 3,3V Laengsregler (LT1086-Serie z.B.)  ||||| =&amp;gt; vgl z.B. [http://reichelt.de/?ARTIKEL=LT%201086%20CM3%2C3 LT 1086 CM3,3] (SMD) oder [http://reichelt.de/?ARTIKEL=LT%201086%20CT3%2C3 LT 1086 CT3,3] (TO-220) bei Reichelt&lt;br /&gt;
* Flexible Messleitungen: Wie gesagt Reichelt bietet ja die ganze Palette an Banenen/Laborsteckern, Krokodielklemmen usw. an nur die Leitungen dazu fehlen im Programm.  (Sind schon im Sortiment. Fertig Konfektionierte zB: ML 100 SW, Meterware zB: MESSLEITUNG 10SW)&lt;br /&gt;
&lt;br /&gt;
==== Nachtrag ====&lt;br /&gt;
&lt;br /&gt;
* lineare Potentiometer als Schiebepoti&lt;br /&gt;
* 3.3V kann jetzt genauer spezifiziert werden: SMD möglichst klein (z.b. SOT23) mit kleiner Low-Drop Spannung, z.B. wie [http://www.torex-usa.com/product/pro02/pro_05.html Torex XC62]], Wichtig auch: geringer Eigenstromverbrauch, weil man die nur so sinnvoll in batteriebetriebenen Geräten nutzen kann, die ständig eine Spannungsversorgung brauchen. Torex X62 hat da nur 2uA!!! &lt;br /&gt;
* dünner isolierter Draht, wie Klingeldraht nur dünner, vielleicht 0.2-0.3mm zum Fädeln von Platinen&lt;/div&gt;</summary>
		<author><name>Oxygene</name></author>
	</entry>
</feed>